mirror of https://github.com/LemmyNet/lemmy.git
Merge branch 'main' into markdown-link-rule
commit
f057abff71
File diff suppressed because it is too large
Load Diff
56
Cargo.toml
56
Cargo.toml
|
@ -70,15 +70,15 @@ lemmy_routes = { version = "=0.19.0-rc.3", path = "./crates/routes" }
|
|||
lemmy_db_views = { version = "=0.19.0-rc.3", path = "./crates/db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.19.0-rc.3", path = "./crates/db_views_actor" }
|
||||
lemmy_db_views_moderator = { version = "=0.19.0-rc.3", path = "./crates/db_views_moderator" }
|
||||
activitypub_federation = { version = "0.5.0-beta.3", default-features = false, features = [
|
||||
activitypub_federation = { git = "https://github.com/LemmyNet/activitypub-federation-rust.git", branch = "webfinger-alphabets", default-features = false, features = [
|
||||
"actix-web",
|
||||
] }
|
||||
diesel = "2.1.0"
|
||||
diesel = "2.1.3"
|
||||
diesel_migrations = "2.1.0"
|
||||
diesel-async = "0.3.1"
|
||||
serde = { version = "1.0.167", features = ["derive"] }
|
||||
serde_with = "3.0.0"
|
||||
actix-web = { version = "4.3.1", default-features = false, features = [
|
||||
diesel-async = "0.3.2"
|
||||
serde = { version = "1.0.189", features = ["derive"] }
|
||||
serde_with = "3.4.0"
|
||||
actix-web = { version = "4.4.0", default-features = false, features = [
|
||||
"macros",
|
||||
"rustls",
|
||||
"compress-brotli",
|
||||
|
@ -86,37 +86,37 @@ actix-web = { version = "4.3.1", default-features = false, features = [
|
|||
"compress-zstd",
|
||||
"cookies",
|
||||
] }
|
||||
tracing = "0.1.37"
|
||||
tracing-actix-web = { version = "0.7.5", default-features = false }
|
||||
tracing = "0.1.40"
|
||||
tracing-actix-web = { version = "0.7.8", default-features = false }
|
||||
tracing-error = "0.2.0"
|
||||
tracing-log = "0.1.3"
|
||||
tracing-log = "0.1.4"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
url = { version = "2.4.0", features = ["serde"] }
|
||||
reqwest = { version = "0.11.18", features = ["json", "blocking", "gzip"] }
|
||||
reqwest-middleware = "0.2.2"
|
||||
reqwest-tracing = "0.4.5"
|
||||
url = { version = "2.4.1", features = ["serde"] }
|
||||
reqwest = { version = "0.11.22", features = ["json", "blocking", "gzip"] }
|
||||
reqwest-middleware = "0.2.4"
|
||||
reqwest-tracing = "0.4.6"
|
||||
clokwerk = "0.4.0"
|
||||
doku = { version = "0.21.1", features = ["url-2"] }
|
||||
bcrypt = "0.15.0"
|
||||
chrono = { version = "0.4.26", features = ["serde"], default-features = false }
|
||||
serde_json = { version = "1.0.100", features = ["preserve_order"] }
|
||||
base64 = "0.21.2"
|
||||
uuid = { version = "1.4.0", features = ["serde", "v4"] }
|
||||
async-trait = "0.1.71"
|
||||
chrono = { version = "0.4.31", features = ["serde"], default-features = false }
|
||||
serde_json = { version = "1.0.107", features = ["preserve_order"] }
|
||||
base64 = "0.21.5"
|
||||
uuid = { version = "1.5.0", features = ["serde", "v4"] }
|
||||
async-trait = "0.1.74"
|
||||
captcha = "0.0.9"
|
||||
anyhow = { version = "1.0.71", features = [
|
||||
anyhow = { version = "1.0.75", features = [
|
||||
"backtrace",
|
||||
] } # backtrace is on by default on nightly, but not stable rust
|
||||
diesel_ltree = "0.3.0"
|
||||
typed-builder = "0.15.0"
|
||||
typed-builder = "0.15.2"
|
||||
serial_test = "2.0.0"
|
||||
tokio = { version = "1.29.1", features = ["full"] }
|
||||
regex = "1.9.0"
|
||||
tokio = { version = "1.33.0", features = ["full"] }
|
||||
regex = "1.10.2"
|
||||
once_cell = "1.18.0"
|
||||
diesel-derive-newtype = "2.1.0"
|
||||
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
||||
strum = "0.25.0"
|
||||
strum_macros = "0.25.1"
|
||||
strum_macros = "0.25.3"
|
||||
itertools = "0.11.0"
|
||||
futures = "0.3.28"
|
||||
http = "0.2.9"
|
||||
|
@ -125,12 +125,12 @@ rosetta-i18n = "0.1.3"
|
|||
opentelemetry = { version = "0.19.0", features = ["rt-tokio"] }
|
||||
tracing-opentelemetry = { version = "0.19.0" }
|
||||
ts-rs = { version = "7.0.0", features = ["serde-compat", "chrono-impl"] }
|
||||
rustls = { version = "0.21.3", features = ["dangerous_configuration"] }
|
||||
rustls = { version = "0.21.8", features = ["dangerous_configuration"] }
|
||||
futures-util = "0.3.28"
|
||||
tokio-postgres = "0.7.8"
|
||||
tokio-postgres = "0.7.10"
|
||||
tokio-postgres-rustls = "0.10.0"
|
||||
urlencoding = "2.1.3"
|
||||
enum-map = "2.6"
|
||||
enum-map = "2.7"
|
||||
|
||||
[dependencies]
|
||||
lemmy_api = { workspace = true }
|
||||
|
@ -162,7 +162,7 @@ tracing-opentelemetry = { workspace = true, optional = true }
|
|||
opentelemetry = { workspace = true, optional = true }
|
||||
console-subscriber = { version = "0.1.10", optional = true }
|
||||
opentelemetry-otlp = { version = "0.12.0", optional = true }
|
||||
pict-rs = { version = "0.4.0-rc.12", optional = true }
|
||||
pict-rs = { version = "0.4.5", optional = true }
|
||||
tokio.workspace = true
|
||||
actix-cors = "0.6.4"
|
||||
rustls = { workspace = true }
|
||||
|
@ -173,5 +173,5 @@ chrono = { workspace = true }
|
|||
prometheus = { version = "0.13.3", features = ["process"], optional = true }
|
||||
actix-web-prom = { version = "0.6.0", optional = true }
|
||||
serial_test = { workspace = true }
|
||||
clap = { version = "4.3.19", features = ["derive"] }
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
actix-web-httpauth = "0.8.1"
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
"api-test": "jest -i follow.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts && jest -i private_message.spec.ts && jest -i user.spec.ts && jest -i community.spec.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/node": "^20.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.5",
|
||||
"@typescript-eslint/parser": "^6.7.5",
|
||||
"eslint": "^8.51.0",
|
||||
"@types/jest": "^29.5.6",
|
||||
"@types/node": "^20.8.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
||||
"@typescript-eslint/parser": "^6.8.0",
|
||||
"eslint": "^8.52.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.19.0-rc.12",
|
||||
|
|
|
@ -25,7 +25,6 @@ import {
|
|||
getCommunityByName,
|
||||
blockInstance,
|
||||
waitUntil,
|
||||
delay,
|
||||
alphaUrl,
|
||||
delta,
|
||||
betaAllowedInstances,
|
||||
|
|
|
@ -17,8 +17,9 @@ import {
|
|||
saveUserSettingsFederated,
|
||||
setupLogins,
|
||||
alphaUrl,
|
||||
saveUserSettings,
|
||||
} from "./shared";
|
||||
import { LemmyHttp } from "lemmy-js-client";
|
||||
import { LemmyHttp, SaveUserSettings } from "lemmy-js-client";
|
||||
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
||||
|
||||
beforeAll(async () => {
|
||||
|
@ -57,6 +58,15 @@ test("Set some user settings, check that they are federated", async () => {
|
|||
let alphaPerson = (await resolvePerson(alpha, apShortname)).person;
|
||||
let betaPerson = (await resolvePerson(beta, apShortname)).person;
|
||||
assertUserFederation(alphaPerson, betaPerson);
|
||||
|
||||
// Catches a bug where when only the person or local_user changed
|
||||
let form: SaveUserSettings = {
|
||||
theme: "test",
|
||||
};
|
||||
await saveUserSettings(beta, form);
|
||||
|
||||
let site = await getSite(beta);
|
||||
expect(site.my_user?.local_user_view.local_user.theme).toBe("test");
|
||||
});
|
||||
|
||||
test("Delete user", async () => {
|
||||
|
@ -119,3 +129,21 @@ test("Requests with invalid auth should be treated as unauthenticated", async ()
|
|||
let posts = invalid_auth.getPosts(form);
|
||||
expect((await posts).posts).toBeDefined();
|
||||
});
|
||||
|
||||
test("Create user with Arabic name", async () => {
|
||||
let userRes = await registerUser(alpha, "تجريب");
|
||||
expect(userRes.jwt).toBeDefined();
|
||||
let user = new LemmyHttp(alphaUrl, {
|
||||
headers: { Authorization: `Bearer ${userRes.jwt ?? ""}` },
|
||||
});
|
||||
|
||||
let site = await getSite(user);
|
||||
expect(site.my_user).toBeDefined();
|
||||
if (!site.my_user) {
|
||||
throw "Missing site user";
|
||||
}
|
||||
apShortname = `@${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
|
||||
|
||||
let alphaPerson = (await resolvePerson(alpha, apShortname)).person;
|
||||
expect(alphaPerson).toBeDefined();
|
||||
});
|
||||
|
|
|
@ -329,17 +329,17 @@
|
|||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@eslint/js@8.51.0":
|
||||
version "8.51.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.51.0.tgz#6d419c240cfb2b66da37df230f7e7eef801c32fa"
|
||||
integrity sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==
|
||||
"@eslint/js@8.52.0":
|
||||
version "8.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.52.0.tgz#78fe5f117840f69dc4a353adf9b9cd926353378c"
|
||||
integrity sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.11":
|
||||
version "0.11.11"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844"
|
||||
integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==
|
||||
"@humanwhocodes/config-array@^0.11.13":
|
||||
version "0.11.13"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297"
|
||||
integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==
|
||||
dependencies:
|
||||
"@humanwhocodes/object-schema" "^1.2.1"
|
||||
"@humanwhocodes/object-schema" "^2.0.1"
|
||||
debug "^4.1.1"
|
||||
minimatch "^3.0.5"
|
||||
|
||||
|
@ -348,10 +348,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
|
||||
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
||||
|
||||
"@humanwhocodes/object-schema@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||
"@humanwhocodes/object-schema@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044"
|
||||
integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
|
@ -704,10 +704,10 @@
|
|||
dependencies:
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest@^29.5.1":
|
||||
version "29.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.5.tgz#727204e06228fe24373df9bae76b90f3e8236a2a"
|
||||
integrity sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==
|
||||
"@types/jest@^29.5.6":
|
||||
version "29.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.6.tgz#f4cf7ef1b5b0bfc1aa744e41b24d9cc52533130b"
|
||||
integrity sha512-/t9NnzkOpXb4Nfvg17ieHE6EeSjDS2SGSpNYfoLbUAeL/EOueU/RSdOWFpfQTXBEM7BguYW1XQ0EbM+6RlIh6w==
|
||||
dependencies:
|
||||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
|
@ -722,10 +722,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.0.tgz#10ddf0119cf20028781c06d7115562934e53f745"
|
||||
integrity sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==
|
||||
|
||||
"@types/node@^20.8.6":
|
||||
version "20.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.6.tgz#0dbd4ebcc82ad0128df05d0e6f57e05359ee47fa"
|
||||
integrity sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==
|
||||
"@types/node@^20.8.7":
|
||||
version "20.8.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.7.tgz#ad23827850843de973096edfc5abc9e922492a25"
|
||||
integrity sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==
|
||||
dependencies:
|
||||
undici-types "~5.25.1"
|
||||
|
||||
|
@ -751,16 +751,16 @@
|
|||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.5.tgz#f4024b9f63593d0c2b5bd6e4ca027e6f30934d4f"
|
||||
integrity sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==
|
||||
"@typescript-eslint/eslint-plugin@^6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz#06abe4265e7c82f20ade2dcc0e3403c32d4f148b"
|
||||
integrity sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.5.1"
|
||||
"@typescript-eslint/scope-manager" "6.7.5"
|
||||
"@typescript-eslint/type-utils" "6.7.5"
|
||||
"@typescript-eslint/utils" "6.7.5"
|
||||
"@typescript-eslint/visitor-keys" "6.7.5"
|
||||
"@typescript-eslint/scope-manager" "6.8.0"
|
||||
"@typescript-eslint/type-utils" "6.8.0"
|
||||
"@typescript-eslint/utils" "6.8.0"
|
||||
"@typescript-eslint/visitor-keys" "6.8.0"
|
||||
debug "^4.3.4"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.2.4"
|
||||
|
@ -768,74 +768,79 @@
|
|||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/parser@^6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.5.tgz#8d7ca3d1fbd9d5a58cc4d30b2aa797a760137886"
|
||||
integrity sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==
|
||||
"@typescript-eslint/parser@^6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.8.0.tgz#bb2a969d583db242f1ee64467542f8b05c2e28cb"
|
||||
integrity sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "6.7.5"
|
||||
"@typescript-eslint/types" "6.7.5"
|
||||
"@typescript-eslint/typescript-estree" "6.7.5"
|
||||
"@typescript-eslint/visitor-keys" "6.7.5"
|
||||
"@typescript-eslint/scope-manager" "6.8.0"
|
||||
"@typescript-eslint/types" "6.8.0"
|
||||
"@typescript-eslint/typescript-estree" "6.8.0"
|
||||
"@typescript-eslint/visitor-keys" "6.8.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz#1cf33b991043886cd67f4f3600b8e122fc14e711"
|
||||
integrity sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==
|
||||
"@typescript-eslint/scope-manager@6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz#5cac7977385cde068ab30686889dd59879811efd"
|
||||
integrity sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.7.5"
|
||||
"@typescript-eslint/visitor-keys" "6.7.5"
|
||||
"@typescript-eslint/types" "6.8.0"
|
||||
"@typescript-eslint/visitor-keys" "6.8.0"
|
||||
|
||||
"@typescript-eslint/type-utils@6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.5.tgz#0a65949ec16588d8956f6d967f7d9c84ddb2d72a"
|
||||
integrity sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==
|
||||
"@typescript-eslint/type-utils@6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz#50365e44918ca0fd159844b5d6ea96789731e11f"
|
||||
integrity sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "6.7.5"
|
||||
"@typescript-eslint/utils" "6.7.5"
|
||||
"@typescript-eslint/typescript-estree" "6.8.0"
|
||||
"@typescript-eslint/utils" "6.8.0"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/types@6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.5.tgz#4571320fb9cf669de9a95d9849f922c3af809790"
|
||||
integrity sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==
|
||||
"@typescript-eslint/types@6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.8.0.tgz#1ab5d4fe1d613e3f65f6684026ade6b94f7e3ded"
|
||||
integrity sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==
|
||||
|
||||
"@typescript-eslint/typescript-estree@6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz#4578de1a26e9f24950f029a4f00d1bfe41f15a39"
|
||||
integrity sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==
|
||||
"@typescript-eslint/typescript-estree@6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz#9565f15e0cd12f55cf5aa0dfb130a6cb0d436ba1"
|
||||
integrity sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.7.5"
|
||||
"@typescript-eslint/visitor-keys" "6.7.5"
|
||||
"@typescript-eslint/types" "6.8.0"
|
||||
"@typescript-eslint/visitor-keys" "6.8.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/utils@6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.5.tgz#ab847b53d6b65e029314b8247c2336843dba81ab"
|
||||
integrity sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==
|
||||
"@typescript-eslint/utils@6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.8.0.tgz#d42939c2074c6b59844d0982ce26a51d136c4029"
|
||||
integrity sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@types/json-schema" "^7.0.12"
|
||||
"@types/semver" "^7.5.0"
|
||||
"@typescript-eslint/scope-manager" "6.7.5"
|
||||
"@typescript-eslint/types" "6.7.5"
|
||||
"@typescript-eslint/typescript-estree" "6.7.5"
|
||||
"@typescript-eslint/scope-manager" "6.8.0"
|
||||
"@typescript-eslint/types" "6.8.0"
|
||||
"@typescript-eslint/typescript-estree" "6.8.0"
|
||||
semver "^7.5.4"
|
||||
|
||||
"@typescript-eslint/visitor-keys@6.7.5":
|
||||
version "6.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz#84c68d6ceb5b12d5246b918b84f2b79affd6c2f1"
|
||||
integrity sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==
|
||||
"@typescript-eslint/visitor-keys@6.8.0":
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz#cffebed56ae99c45eba901c378a6447b06be58b8"
|
||||
integrity sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.7.5"
|
||||
"@typescript-eslint/types" "6.8.0"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
|
||||
"@ungap/structured-clone@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
||||
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
||||
|
||||
acorn-jsx@^5.3.2:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
|
@ -1328,18 +1333,19 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
|
|||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
|
||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||
|
||||
eslint@^8.51.0:
|
||||
version "8.51.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.51.0.tgz#4a82dae60d209ac89a5cff1604fea978ba4950f3"
|
||||
integrity sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==
|
||||
eslint@^8.52.0:
|
||||
version "8.52.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.52.0.tgz#d0cd4a1fac06427a61ef9242b9353f36ea7062fc"
|
||||
integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.2.0"
|
||||
"@eslint-community/regexpp" "^4.6.1"
|
||||
"@eslint/eslintrc" "^2.1.2"
|
||||
"@eslint/js" "8.51.0"
|
||||
"@humanwhocodes/config-array" "^0.11.11"
|
||||
"@eslint/js" "8.52.0"
|
||||
"@humanwhocodes/config-array" "^0.11.13"
|
||||
"@humanwhocodes/module-importer" "^1.0.1"
|
||||
"@nodelib/fs.walk" "^1.2.8"
|
||||
"@ungap/structured-clone" "^1.2.0"
|
||||
ajv "^6.12.4"
|
||||
chalk "^4.0.0"
|
||||
cross-spawn "^7.0.2"
|
||||
|
|
|
@ -34,7 +34,7 @@ chrono = { workspace = true }
|
|||
url = { workspace = true }
|
||||
wav = "1.0.0"
|
||||
sitemap-rs = "0.2.0"
|
||||
totp-rs = { version = "5.0.2", features = ["gen_secret", "otpauth"] }
|
||||
totp-rs = { version = "5.4.0", features = ["gen_secret", "otpauth"] }
|
||||
actix-web-httpauth = "0.8.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -21,7 +21,7 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
|
||||
error::{LemmyError, LemmyErrorType},
|
||||
utils::validation::{is_valid_bio_field, is_valid_display_name, is_valid_matrix_id},
|
||||
};
|
||||
|
||||
|
@ -98,9 +98,11 @@ pub async fn save_user_settings(
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
// Ignore errors, because 'no fields updated' will return an error.
|
||||
// https://github.com/LemmyNet/lemmy/issues/4076
|
||||
Person::update(&mut context.pool(), person_id, &person_form)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::UserAlreadyExists)?;
|
||||
.ok();
|
||||
|
||||
if let Some(discussion_languages) = data.discussion_languages.clone() {
|
||||
LocalUserLanguage::update(&mut context.pool(), discussion_languages, local_user_id).await?;
|
||||
|
@ -128,7 +130,11 @@ pub async fn save_user_settings(
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form).await?;
|
||||
// Ignore errors, because 'no fields updated' will return an error.
|
||||
// https://github.com/LemmyNet/lemmy/issues/4076
|
||||
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
Ok(Json(SuccessResponse::default()))
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ pub async fn leave_admin(
|
|||
local_user_view.local_user.id,
|
||||
&LocalUserUpdateForm {
|
||||
admin: Some(false),
|
||||
// Necessary because admins can bypass the registration applications (if they're turned on)
|
||||
// but then won't be able to log in because they haven't been approved.
|
||||
accepted_application: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
|
|
|
@ -47,6 +47,7 @@ pub struct GetPost {
|
|||
pub comment_id: Option<CommentId>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
|
|
|
@ -8,6 +8,7 @@ use lemmy_utils::{
|
|||
REQWEST_TIMEOUT,
|
||||
};
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use reqwest::{Client, ClientBuilder};
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
use serde::Deserialize;
|
||||
use tracing::info;
|
||||
|
@ -288,12 +289,17 @@ async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Resu
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_user_agent(settings: &Settings) -> String {
|
||||
format!(
|
||||
pub fn client_builder(settings: &Settings) -> ClientBuilder {
|
||||
let user_agent = format!(
|
||||
"Lemmy/{}; +{}",
|
||||
VERSION,
|
||||
settings.get_protocol_and_hostname()
|
||||
)
|
||||
);
|
||||
|
||||
Client::builder()
|
||||
.user_agent(user_agent.clone())
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.connect_timeout(REQWEST_TIMEOUT)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -301,12 +307,7 @@ mod tests {
|
|||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::request::{
|
||||
build_user_agent,
|
||||
fetch_site_metadata,
|
||||
html_to_site_metadata,
|
||||
SiteMetadata,
|
||||
};
|
||||
use crate::request::{client_builder, fetch_site_metadata, html_to_site_metadata, SiteMetadata};
|
||||
use lemmy_utils::settings::SETTINGS;
|
||||
use url::Url;
|
||||
|
||||
|
@ -314,11 +315,7 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn test_site_metadata() {
|
||||
let settings = &SETTINGS.clone();
|
||||
let client = reqwest::Client::builder()
|
||||
.user_agent(build_user_agent(settings))
|
||||
.build()
|
||||
.unwrap()
|
||||
.into();
|
||||
let client = client_builder(settings).build().unwrap().into();
|
||||
let sample_url = Url::parse("https://gitlab.com/IzzyOnDroid/repo/-/wikis/FAQ").unwrap();
|
||||
let sample_res = fetch_site_metadata(&client, &sample_url).await.unwrap();
|
||||
assert_eq!(
|
||||
|
|
|
@ -18,7 +18,7 @@ pub async fn get_private_message(
|
|||
let limit = data.limit;
|
||||
let unread_only = data.unread_only.unwrap_or_default();
|
||||
let creator_id = data.creator_id;
|
||||
let mut messages = PrivateMessageQuery {
|
||||
let messages = PrivateMessageQuery {
|
||||
page,
|
||||
limit,
|
||||
unread_only,
|
||||
|
@ -27,14 +27,6 @@ pub async fn get_private_message(
|
|||
.list(&mut context.pool(), person_id)
|
||||
.await?;
|
||||
|
||||
// Messages sent by ourselves should be marked as read. The `read` column in database is only
|
||||
// for the recipient, and shouldnt be exposed to sender.
|
||||
messages.iter_mut().for_each(|pmv| {
|
||||
if pmv.creator.id == person_id {
|
||||
pmv.private_message.read = true
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Json(PrivateMessagesResponse {
|
||||
private_messages: messages,
|
||||
}))
|
||||
|
|
|
@ -59,10 +59,10 @@ pub(crate) mod tests {
|
|||
|
||||
use activitypub_federation::config::{Data, FederationConfig};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::{context::LemmyContext, request::build_user_agent};
|
||||
use lemmy_api_common::{context::LemmyContext, request::client_builder};
|
||||
use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool_for_tests};
|
||||
use lemmy_utils::{rate_limit::RateLimitCell, settings::SETTINGS};
|
||||
use reqwest::{Client, Request, Response};
|
||||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{ClientBuilder, Middleware, Next};
|
||||
use task_local_extensions::Extensions;
|
||||
|
||||
|
@ -86,11 +86,7 @@ pub(crate) mod tests {
|
|||
// call this to run migrations
|
||||
let pool = build_db_pool_for_tests().await;
|
||||
|
||||
let settings = SETTINGS.clone();
|
||||
let client = Client::builder()
|
||||
.user_agent(build_user_agent(&settings))
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = client_builder(&SETTINGS).build().unwrap();
|
||||
|
||||
let client = ClientBuilder::new(client).with(BlockedMiddleware).build();
|
||||
let secret = Secret {
|
||||
|
|
|
@ -71,8 +71,10 @@ pub struct PersonAggregates {
|
|||
pub id: i32,
|
||||
pub person_id: PersonId,
|
||||
pub post_count: i64,
|
||||
#[serde(skip)]
|
||||
pub post_score: i64,
|
||||
pub comment_count: i64,
|
||||
#[serde(skip)]
|
||||
pub comment_score: i64,
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ pub mod newtypes;
|
|||
pub mod schema;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod aliases {
|
||||
use crate::schema::person;
|
||||
diesel::alias!(person as person1: Person1, person as person2: Person2);
|
||||
use crate::schema::{community_moderator, person};
|
||||
diesel::alias!(person as person1: Person1, person as person2: Person2, community_moderator as community_moderator1: CommunityModerator1);
|
||||
}
|
||||
pub mod source;
|
||||
#[cfg(feature = "full")]
|
||||
|
|
|
@ -12,6 +12,7 @@ use diesel::{
|
|||
use diesel_async::RunQueryDsl;
|
||||
use diesel_ltree::{nlevel, subpath, Ltree, LtreeExtensions};
|
||||
use lemmy_db_schema::{
|
||||
aliases,
|
||||
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
|
||||
schema::{
|
||||
comment,
|
||||
|
@ -90,6 +91,17 @@ fn queries<'a>() -> Queries<
|
|||
.and(community_moderator::person_id.eq(person_id_join)),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
aliases::community_moderator1.on(
|
||||
community::id
|
||||
.eq(aliases::community_moderator1.field(community_moderator::community_id))
|
||||
.and(
|
||||
aliases::community_moderator1
|
||||
.field(community_moderator::person_id)
|
||||
.eq(comment::creator_id),
|
||||
),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let selection = (
|
||||
|
@ -99,6 +111,10 @@ fn queries<'a>() -> Queries<
|
|||
community::all_columns,
|
||||
comment_aggregates::all_columns,
|
||||
community_person_ban::id.nullable().is_not_null(),
|
||||
aliases::community_moderator1
|
||||
.field(community_moderator::id)
|
||||
.nullable()
|
||||
.is_not_null(),
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
comment_saved::id.nullable().is_not_null(),
|
||||
person_block::id.nullable().is_not_null(),
|
||||
|
@ -338,7 +354,7 @@ mod tests {
|
|||
source::{
|
||||
actor_language::LocalUserLanguage,
|
||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
||||
community::{Community, CommunityInsertForm},
|
||||
community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm},
|
||||
instance::Instance,
|
||||
language::Language,
|
||||
local_user::{LocalUser, LocalUserInsertForm},
|
||||
|
@ -346,7 +362,7 @@ mod tests {
|
|||
person_block::{PersonBlock, PersonBlockForm},
|
||||
post::{Post, PostInsertForm},
|
||||
},
|
||||
traits::{Blockable, Crud, Likeable},
|
||||
traits::{Blockable, Crud, Joinable, Likeable},
|
||||
utils::build_db_pool_for_tests,
|
||||
SubscribedType,
|
||||
};
|
||||
|
@ -779,6 +795,30 @@ mod tests {
|
|||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_creator_is_moderator() {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await;
|
||||
|
||||
// Make one of the inserted persons a moderator
|
||||
let person_id = data.inserted_person_2.id;
|
||||
let community_id = data.inserted_community.id;
|
||||
let form = CommunityModeratorForm {
|
||||
community_id,
|
||||
person_id,
|
||||
};
|
||||
CommunityModerator::join(pool, &form).await.unwrap();
|
||||
|
||||
// Make sure that they come back as a mod in the list
|
||||
let comments = CommentQuery::default().list(pool).await.unwrap();
|
||||
|
||||
assert!(comments[1].creator_is_moderator);
|
||||
|
||||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
async fn cleanup(data: Data, pool: &mut DbPool<'_>) {
|
||||
CommentLike::remove(
|
||||
pool,
|
||||
|
@ -814,6 +854,7 @@ mod tests {
|
|||
.unwrap();
|
||||
CommentView {
|
||||
creator_banned_from_community: false,
|
||||
creator_is_moderator: false,
|
||||
my_vote: None,
|
||||
subscribed: SubscribedType::NotSubscribed,
|
||||
saved: false,
|
||||
|
|
|
@ -107,6 +107,13 @@ fn queries<'a>() -> Queries<
|
|||
.and(community_person_ban::person_id.eq(post_aggregates::creator_id)),
|
||||
),
|
||||
);
|
||||
let creator_is_moderator = exists(
|
||||
community_moderator::table.filter(
|
||||
post_aggregates::community_id
|
||||
.eq(community_moderator::community_id)
|
||||
.and(community_moderator::person_id.eq(post_aggregates::creator_id)),
|
||||
),
|
||||
);
|
||||
|
||||
let is_saved = |person_id| {
|
||||
exists(
|
||||
|
@ -226,6 +233,7 @@ fn queries<'a>() -> Queries<
|
|||
person::all_columns,
|
||||
community::all_columns,
|
||||
is_creator_banned_from_community,
|
||||
creator_is_moderator,
|
||||
post_aggregates::all_columns,
|
||||
subscribed_type_selection,
|
||||
is_saved_selection,
|
||||
|
@ -542,16 +550,10 @@ impl PostView {
|
|||
my_person_id: Option<PersonId>,
|
||||
is_mod_or_admin: bool,
|
||||
) -> Result<Self, Error> {
|
||||
let mut res = queries()
|
||||
let res = queries()
|
||||
.read(pool, (post_id, my_person_id, is_mod_or_admin))
|
||||
.await?;
|
||||
|
||||
// If a person is given, then my_vote, if None, should be 0, not null
|
||||
// Necessary to differentiate between other person's votes
|
||||
if my_person_id.is_some() && res.my_vote.is_none() {
|
||||
res.my_vote = Some(0)
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
@ -711,7 +713,7 @@ mod tests {
|
|||
newtypes::LanguageId,
|
||||
source::{
|
||||
actor_language::LocalUserLanguage,
|
||||
community::{Community, CommunityInsertForm},
|
||||
community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm},
|
||||
community_block::{CommunityBlock, CommunityBlockForm},
|
||||
instance::Instance,
|
||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
|
@ -721,7 +723,7 @@ mod tests {
|
|||
person_block::{PersonBlock, PersonBlockForm},
|
||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
|
||||
},
|
||||
traits::{Blockable, Crud, Likeable},
|
||||
traits::{Blockable, Crud, Joinable, Likeable},
|
||||
utils::{build_db_pool_for_tests, DbPool},
|
||||
SortType,
|
||||
SubscribedType,
|
||||
|
@ -877,7 +879,7 @@ mod tests {
|
|||
assert_eq!(1, read_post_listing.len());
|
||||
|
||||
assert_eq!(expected_post_listing_with_user, read_post_listing[0]);
|
||||
expected_post_listing_with_user.my_vote = Some(0);
|
||||
expected_post_listing_with_user.my_vote = None;
|
||||
assert_eq!(
|
||||
expected_post_listing_with_user,
|
||||
post_listing_single_with_person
|
||||
|
@ -1069,6 +1071,36 @@ mod tests {
|
|||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn creator_is_moderator() {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await;
|
||||
|
||||
// Make one of the inserted persons a moderator
|
||||
let person_id = data.local_user_view.person.id;
|
||||
let community_id = data.inserted_community.id;
|
||||
let form = CommunityModeratorForm {
|
||||
community_id,
|
||||
person_id,
|
||||
};
|
||||
CommunityModerator::join(pool, &form).await.unwrap();
|
||||
|
||||
let post_listing = PostQuery {
|
||||
sort: (Some(SortType::New)),
|
||||
community_id: (Some(data.inserted_community.id)),
|
||||
local_user: (Some(&data.local_user_view)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(post_listing[1].creator_is_moderator);
|
||||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn post_listing_person_language() {
|
||||
|
@ -1402,6 +1434,7 @@ mod tests {
|
|||
last_refreshed_at: inserted_person.last_refreshed_at,
|
||||
},
|
||||
creator_banned_from_community: false,
|
||||
creator_is_moderator: false,
|
||||
community: Community {
|
||||
id: inserted_community.id,
|
||||
name: inserted_community.name.clone(),
|
||||
|
|
|
@ -12,7 +12,7 @@ use diesel_async::RunQueryDsl;
|
|||
use lemmy_db_schema::{
|
||||
aliases,
|
||||
newtypes::{PersonId, PrivateMessageId},
|
||||
schema::{person, private_message},
|
||||
schema::{person, person_block, private_message},
|
||||
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||
};
|
||||
use tracing::debug;
|
||||
|
@ -27,6 +27,13 @@ fn queries<'a>() -> Queries<
|
|||
.inner_join(
|
||||
aliases::person1.on(private_message::recipient_id.eq(aliases::person1.field(person::id))),
|
||||
)
|
||||
.left_join(
|
||||
person_block::table.on(
|
||||
private_message::creator_id
|
||||
.eq(person_block::target_id)
|
||||
.and(person_block::person_id.eq(aliases::person1.field(person::id))),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let selection = (
|
||||
|
@ -45,7 +52,10 @@ fn queries<'a>() -> Queries<
|
|||
|
||||
let list = move |mut conn: DbConn<'a>,
|
||||
(options, recipient_id): (PrivateMessageQuery, PersonId)| async move {
|
||||
let mut query = all_joins(private_message::table.into_boxed()).select(selection);
|
||||
let mut query = all_joins(private_message::table.into_boxed())
|
||||
.select(selection)
|
||||
// Dont show replies from blocked users
|
||||
.filter(person_block::person_id.is_null());
|
||||
|
||||
// If its unread, I only want the ones to me
|
||||
if options.unread_only {
|
||||
|
@ -106,6 +116,15 @@ impl PrivateMessageView {
|
|||
use diesel::dsl::count;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
private_message::table
|
||||
.left_join(
|
||||
person_block::table.on(
|
||||
private_message::creator_id
|
||||
.eq(person_block::target_id)
|
||||
.and(person_block::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
// Dont count replies from blocked users
|
||||
.filter(person_block::person_id.is_null())
|
||||
.filter(private_message::read.eq(false))
|
||||
.filter(private_message::recipient_id.eq(my_person_id))
|
||||
.filter(private_message::deleted.eq(false))
|
||||
|
@ -138,14 +157,15 @@ mod tests {
|
|||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::private_message_view::PrivateMessageQuery;
|
||||
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
instance::Instance,
|
||||
person::{Person, PersonInsertForm},
|
||||
person_block::{PersonBlock, PersonBlockForm},
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
traits::{Blockable, Crud},
|
||||
utils::build_db_pool_for_tests,
|
||||
};
|
||||
use serial_test::serial;
|
||||
|
@ -280,5 +300,39 @@ mod tests {
|
|||
assert_eq!(timmy_sara_unread_messages.len(), 1);
|
||||
assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id);
|
||||
assert_eq!(timmy_sara_unread_messages[0].recipient.id, timmy.id);
|
||||
|
||||
// Make sure blocks are working
|
||||
let timmy_blocks_sara_form = PersonBlockForm {
|
||||
person_id: timmy.id,
|
||||
target_id: sara.id,
|
||||
};
|
||||
|
||||
let inserted_block = PersonBlock::block(pool, &timmy_blocks_sara_form)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let expected_block = PersonBlock {
|
||||
id: inserted_block.id,
|
||||
person_id: timmy.id,
|
||||
target_id: sara.id,
|
||||
published: inserted_block.published,
|
||||
};
|
||||
assert_eq!(expected_block, inserted_block);
|
||||
|
||||
let timmy_messages = PrivateMessageQuery {
|
||||
unread_only: true,
|
||||
creator_id: Option::None,
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool, timmy.id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(timmy_messages.len(), 1);
|
||||
|
||||
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(timmy_unread_messages, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ pub struct CommentView {
|
|||
pub community: Community,
|
||||
pub counts: CommentAggregates,
|
||||
pub creator_banned_from_community: bool,
|
||||
pub creator_is_moderator: bool,
|
||||
pub subscribed: SubscribedType,
|
||||
pub saved: bool,
|
||||
pub creator_blocked: bool,
|
||||
|
@ -107,6 +108,7 @@ pub struct PostView {
|
|||
pub creator: Person,
|
||||
pub community: Community,
|
||||
pub creator_banned_from_community: bool,
|
||||
pub creator_is_moderator: bool,
|
||||
pub counts: PostAggregates,
|
||||
pub subscribed: SubscribedType,
|
||||
pub saved: bool,
|
||||
|
|
|
@ -31,3 +31,7 @@ ts-rs = { workspace = true, optional = true }
|
|||
chrono.workspace = true
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
|
|
@ -164,6 +164,15 @@ impl CommentReplyView {
|
|||
|
||||
comment_reply::table
|
||||
.inner_join(comment::table)
|
||||
.left_join(
|
||||
person_block::table.on(
|
||||
comment::creator_id
|
||||
.eq(person_block::target_id)
|
||||
.and(person_block::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
// Dont count replies from blocked users
|
||||
.filter(person_block::person_id.is_null())
|
||||
.filter(comment_reply::recipient_id.eq(my_person_id))
|
||||
.filter(comment_reply::read.eq(false))
|
||||
.filter(comment::deleted.eq(false))
|
||||
|
|
|
@ -52,6 +52,7 @@ fn queries<'a>(
|
|||
query
|
||||
.inner_join(person_aggregates::table)
|
||||
.left_join(local_user::table)
|
||||
.filter(person::deleted.eq(false))
|
||||
.select((person::all_columns, person_aggregates::all_columns))
|
||||
};
|
||||
|
||||
|
@ -151,3 +152,165 @@ impl PersonQuery {
|
|||
queries().list(pool, ListMode::Query(self)).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use diesel::NotFound;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
instance::Instance,
|
||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||
person::{Person, PersonInsertForm, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::build_db_pool_for_tests,
|
||||
};
|
||||
use serial_test::serial;
|
||||
|
||||
struct Data {
|
||||
alice: Person,
|
||||
alice_local_user: LocalUser,
|
||||
bob: Person,
|
||||
bob_local_user: LocalUser,
|
||||
}
|
||||
|
||||
async fn init_data(pool: &mut DbPool<'_>) -> Data {
|
||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let alice_form = PersonInsertForm::builder()
|
||||
.name("alice".to_string())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(inserted_instance.id)
|
||||
.build();
|
||||
let alice = Person::create(pool, &alice_form).await.unwrap();
|
||||
let alice_local_user_form = LocalUserInsertForm::builder()
|
||||
.person_id(alice.id)
|
||||
.password_encrypted(String::new())
|
||||
.build();
|
||||
let alice_local_user = LocalUser::create(pool, &alice_local_user_form)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let bob_form = PersonInsertForm::builder()
|
||||
.name("bob".to_string())
|
||||
.bot_account(Some(true))
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(inserted_instance.id)
|
||||
.build();
|
||||
let bob = Person::create(pool, &bob_form).await.unwrap();
|
||||
let bob_local_user_form = LocalUserInsertForm::builder()
|
||||
.person_id(bob.id)
|
||||
.password_encrypted(String::new())
|
||||
.build();
|
||||
let bob_local_user = LocalUser::create(pool, &bob_local_user_form).await.unwrap();
|
||||
|
||||
Data {
|
||||
alice,
|
||||
alice_local_user,
|
||||
bob,
|
||||
bob_local_user,
|
||||
}
|
||||
}
|
||||
|
||||
async fn cleanup(data: Data, pool: &mut DbPool<'_>) {
|
||||
LocalUser::delete(pool, data.alice_local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
LocalUser::delete(pool, data.bob_local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Person::delete(pool, data.alice.id).await.unwrap();
|
||||
Person::delete(pool, data.bob.id).await.unwrap();
|
||||
Instance::delete(pool, data.bob.instance_id).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn exclude_deleted() {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await;
|
||||
|
||||
Person::update(
|
||||
pool,
|
||||
data.alice.id,
|
||||
&PersonUpdateForm {
|
||||
deleted: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let read = PersonView::read(pool, data.alice.id).await;
|
||||
assert_eq!(read.err(), Some(NotFound));
|
||||
|
||||
let list = PersonQuery::default().list(pool).await.unwrap();
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list[0].person.id, data.bob.id);
|
||||
|
||||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn list_banned() {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await;
|
||||
|
||||
Person::update(
|
||||
pool,
|
||||
data.alice.id,
|
||||
&PersonUpdateForm {
|
||||
banned: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let list = PersonView::banned(pool).await.unwrap();
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list[0].person.id, data.alice.id);
|
||||
|
||||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn list_admins() {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await;
|
||||
|
||||
LocalUser::update(
|
||||
pool,
|
||||
data.alice_local_user.id,
|
||||
&LocalUserUpdateForm {
|
||||
admin: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let list = PersonView::admins(pool).await.unwrap();
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list[0].person.id, data.alice.id);
|
||||
|
||||
let is_admin = PersonView::is_admin(pool, data.alice.id).await.unwrap();
|
||||
assert!(is_admin);
|
||||
|
||||
let is_admin = PersonView::is_admin(pool, data.bob.id).await.unwrap();
|
||||
assert!(!is_admin);
|
||||
|
||||
cleanup(data, pool).await;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,12 +30,12 @@ serde.workspace = true
|
|||
tokio = { workspace = true, features = ["full"] }
|
||||
tracing.workspace = true
|
||||
|
||||
async-trait = "0.1.71"
|
||||
bytes = "1.4.0"
|
||||
async-trait = "0.1.74"
|
||||
bytes = "1.5.0"
|
||||
enum_delegate = "0.2.0"
|
||||
moka = { version = "0.11.2", features = ["future"] }
|
||||
openssl = "0.10.55"
|
||||
reqwest-middleware = "0.2.2"
|
||||
reqwest-tracing = "0.4.5"
|
||||
tokio-util = "0.7.8"
|
||||
moka = { version = "0.11.3", features = ["future"] }
|
||||
openssl = "0.10.57"
|
||||
reqwest-middleware = "0.2.4"
|
||||
reqwest-tracing = "0.4.6"
|
||||
tokio-util = "0.7.9"
|
||||
tracing-subscriber = "0.3.17"
|
||||
|
|
|
@ -31,4 +31,4 @@ once_cell = { workspace = true }
|
|||
tracing = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
rss = "2.0.4"
|
||||
rss = "2.0.6"
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::local_user_view_from_jwt;
|
|||
use actix_web::{error::ErrorBadRequest, web, Error, HttpRequest, HttpResponse, Result};
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, Utc};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::check_private_instance};
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, person::Person},
|
||||
traits::ApubActor,
|
||||
|
@ -132,6 +132,8 @@ async fn get_feed_data(
|
|||
) -> Result<HttpResponse, LemmyError> {
|
||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
|
||||
check_private_instance(&None, &site_view.local_site)?;
|
||||
|
||||
let posts = PostQuery {
|
||||
listing_type: (Some(listing_type)),
|
||||
sort: (Some(sort_type)),
|
||||
|
@ -235,6 +237,8 @@ async fn get_feed_user(
|
|||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
let person = Person::read_from_name(&mut context.pool(), user_name, false).await?;
|
||||
|
||||
check_private_instance(&None, &site_view.local_site)?;
|
||||
|
||||
let posts = PostQuery {
|
||||
listing_type: (Some(ListingType::All)),
|
||||
sort: (Some(*sort_type)),
|
||||
|
@ -269,6 +273,8 @@ async fn get_feed_community(
|
|||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
let community = Community::read_from_name(&mut context.pool(), community_name, false).await?;
|
||||
|
||||
check_private_instance(&None, &site_view.local_site)?;
|
||||
|
||||
let posts = PostQuery {
|
||||
sort: (Some(*sort_type)),
|
||||
community_id: (Some(community.id)),
|
||||
|
@ -306,6 +312,8 @@ async fn get_feed_front(
|
|||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||
let local_user = local_user_view_from_jwt(jwt, context).await?;
|
||||
|
||||
check_private_instance(&Some(local_user.clone()), &site_view.local_site)?;
|
||||
|
||||
let posts = PostQuery {
|
||||
listing_type: (Some(ListingType::Subscribed)),
|
||||
local_user: (Some(&local_user)),
|
||||
|
@ -343,6 +351,8 @@ async fn get_feed_inbox(context: &LemmyContext, jwt: &str) -> Result<ChannelBuil
|
|||
|
||||
let sort = CommentSortType::New;
|
||||
|
||||
check_private_instance(&Some(local_user.clone()), &site_view.local_site)?;
|
||||
|
||||
let replies = CommentReplyQuery {
|
||||
recipient_id: (Some(person_id)),
|
||||
my_person_id: (Some(person_id)),
|
||||
|
|
|
@ -23,8 +23,13 @@ use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||
pub fn config(
|
||||
cfg: &mut web::ServiceConfig,
|
||||
client: ClientWithMiddleware,
|
||||
rate_limit: &RateLimitCell,
|
||||
) {
|
||||
cfg
|
||||
.app_data(web::Data::new(client))
|
||||
.service(
|
||||
web::resource("/pictrs/image")
|
||||
.wrap(rate_limit.image())
|
||||
|
@ -90,13 +95,14 @@ async fn upload(
|
|||
body: web::Payload,
|
||||
// require login
|
||||
local_user_view: LocalUserView,
|
||||
client: web::Data<ClientWithMiddleware>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
// TODO: check rate limit here
|
||||
let pictrs_config = context.settings().pictrs_config()?;
|
||||
let image_url = format!("{}image", pictrs_config.url);
|
||||
|
||||
let mut client_req = adapt_request(&req, context.client(), image_url);
|
||||
let mut client_req = adapt_request(&req, &client, image_url);
|
||||
|
||||
if let Some(addr) = req.head().peer_addr {
|
||||
client_req = client_req.header("X-Forwarded-For", addr.to_string())
|
||||
|
@ -130,6 +136,7 @@ async fn full_res(
|
|||
filename: web::Path<String>,
|
||||
web::Query(params): web::Query<PictrsParams>,
|
||||
req: HttpRequest,
|
||||
client: web::Data<ClientWithMiddleware>,
|
||||
context: web::Data<LemmyContext>,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
|
@ -160,7 +167,7 @@ async fn full_res(
|
|||
url
|
||||
};
|
||||
|
||||
image(url, req, context.client()).await
|
||||
image(url, req, &client).await
|
||||
}
|
||||
|
||||
async fn image(
|
||||
|
@ -196,6 +203,7 @@ async fn image(
|
|||
async fn delete(
|
||||
components: web::Path<(String, String)>,
|
||||
req: HttpRequest,
|
||||
client: web::Data<ClientWithMiddleware>,
|
||||
context: web::Data<LemmyContext>,
|
||||
// require login
|
||||
_local_user_view: LocalUserView,
|
||||
|
@ -205,7 +213,7 @@ async fn delete(
|
|||
let pictrs_config = context.settings().pictrs_config()?;
|
||||
let url = format!("{}image/delete/{}/{}", pictrs_config.url, &token, &file);
|
||||
|
||||
let mut client_req = adapt_request(&req, context.client(), url);
|
||||
let mut client_req = adapt_request(&req, &client, url);
|
||||
|
||||
if let Some(addr) = req.head().peer_addr {
|
||||
client_req = client_req.header("X-Forwarded-For", addr.to_string());
|
||||
|
|
|
@ -17,6 +17,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||
"/nodeinfo/2.0.json",
|
||||
web::get().to(node_info).wrap(cache_1hour()),
|
||||
)
|
||||
.service(web::redirect("/version", "/nodeinfo/2.0.json"))
|
||||
.route(
|
||||
"/.well-known/nodeinfo",
|
||||
web::get().to(node_info_well_known).wrap(cache_3days()),
|
||||
|
|
|
@ -41,7 +41,7 @@ typed-builder = { workspace = true }
|
|||
percent-encoding = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
openssl = "0.10.55"
|
||||
openssl = "0.10.57"
|
||||
html2text = "0.6.0"
|
||||
deser-hjson = "1.2.0"
|
||||
smart-default = "0.7.1"
|
||||
|
|
|
@ -56,6 +56,9 @@ impl Display for LemmyError {
|
|||
|
||||
impl actix_web::error::ResponseError for LemmyError {
|
||||
fn status_code(&self) -> http::StatusCode {
|
||||
if self.error_type == LemmyErrorType::IncorrectLogin {
|
||||
return http::StatusCode::UNAUTHORIZED;
|
||||
}
|
||||
match self.inner.downcast_ref::<diesel::result::Error>() {
|
||||
Some(diesel::result::Error::NotFound) => http::StatusCode::NOT_FOUND,
|
||||
_ => http::StatusCode::BAD_REQUEST,
|
||||
|
|
|
@ -4,18 +4,20 @@ use once_cell::sync::Lazy;
|
|||
use regex::{Regex, RegexBuilder};
|
||||
use url::Url;
|
||||
|
||||
static VALID_ACTOR_NAME_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[a-zA-Z0-9_]{3,}$").expect("compile regex"));
|
||||
static VALID_POST_TITLE_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r".*\S{3,200}.*").expect("compile regex"));
|
||||
|
||||
// From here: https://github.com/vector-im/element-android/blob/develop/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt#L35
|
||||
static VALID_MATRIX_ID_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex")
|
||||
Regex::new(r"^@[A-Za-z0-9\\x21-\\x39\\x3B-\\x7F]+:[A-Za-z0-9.-]+(:[0-9]{2,5})?$")
|
||||
.expect("compile regex")
|
||||
});
|
||||
// taken from https://en.wikipedia.org/wiki/UTM_parameters
|
||||
static CLEAN_URL_PARAMS_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(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 BODY_MAX_LENGTH: usize = 10000;
|
||||
const POST_BODY_MAX_LENGTH: usize = 50000;
|
||||
|
@ -85,23 +87,52 @@ fn has_newline(name: &str) -> bool {
|
|||
}
|
||||
|
||||
pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> LemmyResult<()> {
|
||||
let check = name.chars().count() <= actor_name_max_length
|
||||
&& VALID_ACTOR_NAME_REGEX.is_match(name)
|
||||
&& !has_newline(name);
|
||||
if !check {
|
||||
static VALID_ACTOR_NAME_REGEX_EN: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[a-zA-Z0-9_]{3,}$").expect("compile regex"));
|
||||
static VALID_ACTOR_NAME_REGEX_AR: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[\p{Arabic}0-9_]{3,}$").expect("compile regex"));
|
||||
static VALID_ACTOR_NAME_REGEX_RU: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[\p{Cyrillic}0-9_]{3,}$").expect("compile regex"));
|
||||
|
||||
let check = name.chars().count() <= actor_name_max_length && !has_newline(name);
|
||||
|
||||
// Only allow characters from a single alphabet per username. This avoids problems with lookalike
|
||||
// characters like `o` which looks identical in Latin and Cyrillic, and can be used to imitate
|
||||
// other users. Checks for additional alphabets can be added in the same way.
|
||||
let lang_check = VALID_ACTOR_NAME_REGEX_EN.is_match(name)
|
||||
|| VALID_ACTOR_NAME_REGEX_AR.is_match(name)
|
||||
|| VALID_ACTOR_NAME_REGEX_RU.is_match(name);
|
||||
|
||||
if !check || !lang_check {
|
||||
Err(LemmyErrorType::InvalidName.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn has_3_permitted_display_chars(name: &str) -> bool {
|
||||
let mut num_non_fdc: i8 = 0;
|
||||
for c in name.chars() {
|
||||
if !FORBIDDEN_DISPLAY_CHARS.contains(&c) {
|
||||
num_non_fdc += 1;
|
||||
if num_non_fdc >= 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if num_non_fdc >= 3 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Can't do a regex here, reverse lookarounds not supported
|
||||
pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> LemmyResult<()> {
|
||||
let check = !name.contains(FORBIDDEN_DISPLAY_CHARS)
|
||||
&& !name.starts_with('@')
|
||||
&& name.chars().count() >= 3
|
||||
let check = !name.starts_with('@')
|
||||
&& !name.starts_with(FORBIDDEN_DISPLAY_CHARS)
|
||||
&& name.chars().count() <= actor_name_max_length
|
||||
&& !has_newline(name);
|
||||
&& !has_newline(name)
|
||||
&& has_3_permitted_display_chars(name);
|
||||
if !check {
|
||||
Err(LemmyErrorType::InvalidDisplayName.into())
|
||||
} else {
|
||||
|
@ -247,7 +278,7 @@ pub fn check_site_visibility_valid(
|
|||
|
||||
pub fn check_url_scheme(url: &Option<Url>) -> LemmyResult<()> {
|
||||
if let Some(url) = url {
|
||||
if url.scheme() != "http" && url.scheme() != "https" {
|
||||
if !ALLOWED_POST_URL_SCHEMES.contains(&url.scheme()) {
|
||||
Err(LemmyErrorType::InvalidUrlScheme.into())
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -309,8 +340,18 @@ mod tests {
|
|||
let actor_name_max_length = 20;
|
||||
assert!(is_valid_actor_name("Hello_98", actor_name_max_length).is_ok());
|
||||
assert!(is_valid_actor_name("ten", actor_name_max_length).is_ok());
|
||||
assert!(is_valid_actor_name("تجريب", actor_name_max_length).is_ok());
|
||||
assert!(is_valid_actor_name("تجريب_123", actor_name_max_length).is_ok());
|
||||
assert!(is_valid_actor_name("Владимир", actor_name_max_length).is_ok());
|
||||
|
||||
// mixed scripts
|
||||
assert!(is_valid_actor_name("تجريب_abc", actor_name_max_length).is_err());
|
||||
assert!(is_valid_actor_name("Влад_abc", actor_name_max_length).is_err());
|
||||
// dash
|
||||
assert!(is_valid_actor_name("Hello-98", actor_name_max_length).is_err());
|
||||
// too short
|
||||
assert!(is_valid_actor_name("a", actor_name_max_length).is_err());
|
||||
// empty
|
||||
assert!(is_valid_actor_name("", actor_name_max_length).is_err());
|
||||
}
|
||||
|
||||
|
@ -319,6 +360,13 @@ mod tests {
|
|||
let actor_name_max_length = 20;
|
||||
assert!(is_valid_display_name("hello @there", actor_name_max_length).is_ok());
|
||||
assert!(is_valid_display_name("@hello there", actor_name_max_length).is_err());
|
||||
assert!(is_valid_display_name("\u{200d}hello", actor_name_max_length).is_err());
|
||||
assert!(is_valid_display_name(
|
||||
"\u{1f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f}Name",
|
||||
actor_name_max_length
|
||||
)
|
||||
.is_ok());
|
||||
assert!(is_valid_display_name("\u{2003}1\u{ffa0}2\u{200d}", actor_name_max_length).is_err());
|
||||
|
||||
// Make sure zero-space with an @ doesn't work
|
||||
assert!(
|
||||
|
@ -336,9 +384,11 @@ mod tests {
|
|||
#[test]
|
||||
fn test_valid_matrix_id() {
|
||||
assert!(is_valid_matrix_id("@dess:matrix.org").is_ok());
|
||||
assert!(is_valid_matrix_id("@dess:matrix.org:443").is_ok());
|
||||
assert!(is_valid_matrix_id("dess:matrix.org").is_err());
|
||||
assert!(is_valid_matrix_id(" @dess:matrix.org").is_err());
|
||||
assert!(is_valid_matrix_id("@dess:matrix.org t").is_err());
|
||||
assert!(is_valid_matrix_id("@dess:matrix.org t").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -472,7 +522,11 @@ mod tests {
|
|||
assert!(check_url_scheme(&None).is_ok());
|
||||
assert!(check_url_scheme(&Some(Url::parse("http://example.com").unwrap())).is_ok());
|
||||
assert!(check_url_scheme(&Some(Url::parse("https://example.com").unwrap())).is_ok());
|
||||
assert!(check_url_scheme(&Some(Url::parse("https://example.com").unwrap())).is_ok());
|
||||
assert!(check_url_scheme(&Some(Url::parse("ftp://example.com").unwrap())).is_err());
|
||||
assert!(check_url_scheme(&Some(Url::parse("javascript:void").unwrap())).is_err());
|
||||
|
||||
let magnet_link="magnet:?xt=urn:btih:4b390af3891e323778959d5abfff4b726510f14c&dn=Ravel%20Complete%20Piano%20Sheet%20Music%20-%20Public%20Domain&tr=udp%3A%2F%2Fopen.tracker.cl%3A1337%2Fannounce";
|
||||
assert!(check_url_scheme(&Some(Url::parse(magnet_link).unwrap())).is_ok());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ services:
|
|||
|
||||
lemmy-ui:
|
||||
# use "image" to pull down an already compiled lemmy-ui. make sure to comment out "build".
|
||||
image: dessalines/lemmy-ui:0.18.4
|
||||
image: dessalines/lemmy-ui:0.19.0-rc.3
|
||||
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
|
||||
# use "build" to build your local lemmy ui image for development. make sure to comment out "image".
|
||||
# run: docker compose up --build
|
||||
|
|
|
@ -45,9 +45,9 @@ if [ "$ARCH" = 'arm64' ]; then
|
|||
fi
|
||||
|
||||
echo "$LOG_PREFIX Initializing images in the background. Please be patient if compiling from source..."
|
||||
docker compose up -d --build
|
||||
docker compose up --build
|
||||
else
|
||||
sudo docker compose up -d --build
|
||||
sudo docker compose up --build
|
||||
fi
|
||||
|
||||
echo "$LOG_PREFIX Complete! You can now access the UI at http://localhost:1236."
|
||||
|
|
|
@ -56,7 +56,7 @@ http {
|
|||
}
|
||||
|
||||
# backend
|
||||
location ~ ^/(api|pictrs|feeds|nodeinfo|.well-known) {
|
||||
location ~ ^/(api|pictrs|feeds|nodeinfo|version|.well-known) {
|
||||
proxy_pass "http://lemmy";
|
||||
# proxy common stuff
|
||||
proxy_http_version 1.1;
|
||||
|
|
48
src/lib.rs
48
src/lib.rs
|
@ -28,7 +28,7 @@ use clap::{ArgAction, Parser};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
lemmy_db_views::structs::SiteView,
|
||||
request::build_user_agent,
|
||||
request::client_builder,
|
||||
send_activity::{ActivityChannel, MATCH_OUTGOING_ACTIVITIES},
|
||||
utils::{
|
||||
check_private_instance_and_federation_enabled,
|
||||
|
@ -52,11 +52,10 @@ use lemmy_utils::{
|
|||
response::jsonify_plain_text_errors,
|
||||
settings::{structs::Settings, SETTINGS},
|
||||
};
|
||||
use reqwest::Client;
|
||||
use reqwest_middleware::ClientBuilder;
|
||||
use reqwest_tracing::TracingMiddleware;
|
||||
use serde_json::json;
|
||||
use std::{env, ops::Deref, time::Duration};
|
||||
use std::{env, ops::Deref};
|
||||
use tokio::signal::unix::SignalKind;
|
||||
use tracing::subscriber::set_global_default;
|
||||
use tracing_actix_web::TracingLogger;
|
||||
|
@ -112,13 +111,9 @@ pub struct CmdArgs {
|
|||
#[arg(long, default_value_t = 1)]
|
||||
federate_process_count: i32,
|
||||
}
|
||||
/// Max timeout for http requests
|
||||
pub(crate) const REQWEST_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
/// Placing the main function in lib.rs allows other crates to import it and embed Lemmy
|
||||
pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> {
|
||||
let settings = SETTINGS.to_owned();
|
||||
|
||||
// return error 503 while running db migrations and startup tasks
|
||||
let mut startup_server_handle = None;
|
||||
if args.http_server {
|
||||
|
@ -126,14 +121,14 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> {
|
|||
}
|
||||
|
||||
// Run the DB migrations
|
||||
let db_url = get_database_url(Some(&settings));
|
||||
let db_url = get_database_url(Some(&SETTINGS));
|
||||
run_migrations(&db_url);
|
||||
|
||||
// Set up the connection pool
|
||||
let pool = build_db_pool(&settings).await?;
|
||||
let pool = build_db_pool(&SETTINGS).await?;
|
||||
|
||||
// Run the Code-required migrations
|
||||
run_advanced_migrations(&mut (&pool).into(), &settings).await?;
|
||||
run_advanced_migrations(&mut (&pool).into(), &SETTINGS).await?;
|
||||
|
||||
// Initialize the secrets
|
||||
let secret = Secret::init(&mut (&pool).into())
|
||||
|
@ -148,7 +143,7 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> {
|
|||
let federation_enabled = local_site.federation_enabled;
|
||||
|
||||
if federation_enabled {
|
||||
println!("federation enabled, host is {}", &settings.hostname);
|
||||
println!("federation enabled, host is {}", &SETTINGS.hostname);
|
||||
}
|
||||
|
||||
check_private_instance_and_federation_enabled(&local_site)?;
|
||||
|
@ -160,20 +155,12 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> {
|
|||
|
||||
println!(
|
||||
"Starting http server at {}:{}",
|
||||
settings.bind, settings.port
|
||||
SETTINGS.bind, SETTINGS.port
|
||||
);
|
||||
|
||||
let user_agent = build_user_agent(&settings);
|
||||
let reqwest_client = Client::builder()
|
||||
.user_agent(user_agent.clone())
|
||||
.timeout(REQWEST_TIMEOUT)
|
||||
.connect_timeout(REQWEST_TIMEOUT)
|
||||
.build()?;
|
||||
|
||||
let client = ClientBuilder::new(reqwest_client.clone())
|
||||
let client = ClientBuilder::new(client_builder(&SETTINGS).build()?)
|
||||
.with(TracingMiddleware::default())
|
||||
.build();
|
||||
|
||||
let context = LemmyContext::create(
|
||||
pool.clone(),
|
||||
client.clone(),
|
||||
|
@ -187,10 +174,10 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "prometheus-metrics")]
|
||||
serve_prometheus(settings.prometheus.as_ref(), context.clone());
|
||||
serve_prometheus(SETTINGS.prometheus.as_ref(), context.clone());
|
||||
|
||||
let federation_config = FederationConfig::builder()
|
||||
.domain(settings.hostname.clone())
|
||||
.domain(SETTINGS.hostname.clone())
|
||||
.app_data(context.clone())
|
||||
.client(client.clone())
|
||||
.http_fetch_limit(FEDERATION_HTTP_FETCH_LIMIT)
|
||||
|
@ -212,9 +199,10 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> {
|
|||
if let Some(startup_server_handle) = startup_server_handle {
|
||||
startup_server_handle.stop(true).await;
|
||||
}
|
||||
|
||||
Some(create_http_server(
|
||||
federation_config.clone(),
|
||||
settings.clone(),
|
||||
SETTINGS.clone(),
|
||||
federation_enabled,
|
||||
)?)
|
||||
} else {
|
||||
|
@ -293,6 +281,12 @@ fn create_http_server(
|
|||
let context: LemmyContext = federation_config.deref().clone();
|
||||
let rate_limit_cell = federation_config.rate_limit_cell().clone();
|
||||
let self_origin = settings.get_protocol_and_hostname();
|
||||
|
||||
// Pictrs cannot use proxy
|
||||
let pictrs_client = ClientBuilder::new(client_builder(&SETTINGS).no_proxy().build()?)
|
||||
.with(TracingMiddleware::default())
|
||||
.build();
|
||||
|
||||
// Create Http server with websocket support
|
||||
let server = HttpServer::new(move || {
|
||||
let cors_origin = env::var("LEMMY_CORS_ORIGIN");
|
||||
|
@ -335,7 +329,7 @@ fn create_http_server(
|
|||
}
|
||||
})
|
||||
.configure(feeds::config)
|
||||
.configure(|cfg| images::config(cfg, &rate_limit_cell))
|
||||
.configure(|cfg| images::config(cfg, pictrs_client.clone(), &rate_limit_cell))
|
||||
.configure(nodeinfo::config)
|
||||
})
|
||||
.disable_signals()
|
||||
|
@ -358,9 +352,9 @@ pub fn init_logging(opentelemetry_url: &Option<Url>) -> Result<(), LemmyError> {
|
|||
|
||||
let format_layer = {
|
||||
#[cfg(feature = "json-log")]
|
||||
let layer = tracing_subscriber::fmt::layer().json();
|
||||
let layer = tracing_subscriber::fmt::layer().with_ansi(false).json();
|
||||
#[cfg(not(feature = "json-log"))]
|
||||
let layer = tracing_subscriber::fmt::layer();
|
||||
let layer = tracing_subscriber::fmt::layer().with_ansi(false);
|
||||
|
||||
layer.with_filter(targets.clone())
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue