postgres:
image: postgres:12-alpine
+ ports:
+ - "127.0.0.1:5432:5432"
environment:
- POSTGRES_USER=lemmy
- POSTGRES_PASSWORD=password
cargo build
popd
+pushd ../../ui
+yarn
+popd
+
+mkdir -p volumes/pictrs_{alpha,beta,gamma}
+sudo chown -R 991:991 volumes/pictrs_{alpha,beta,gamma}
+
sudo docker build ../../ --file ../federation/Dockerfile --tag lemmy-federation:latest
-for Item in alpha beta gamma ; do
- sudo mkdir -p volumes/pictrs_$Item
- sudo chown -R 991:991 volumes/pictrs_$Item
-done
+sudo mkdir -p volumes/pictrs_alpha
+sudo chown -R 991:991 volumes/pictrs_alpha
sudo docker-compose --file ../federation/docker-compose.yml --project-directory . up -d
pushd ../../ui
-yarn
echo "Waiting for Lemmy to start..."
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8540/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8550/api/v1/site')" != "200" ]]; do sleep 1; done
--- /dev/null
+#!/bin/bash
+set -e
+
+sudo rm -rf volumes
+
+pushd ../../server/
+cargo build
+popd
+
+pushd ../../ui
+yarn
+popd
+
+mkdir -p volumes/pictrs_{alpha,beta,gamma}
+sudo chown -R 991:991 volumes/pictrs_{alpha,beta,gamma}
+
+sudo docker build ../../ --file ../federation/Dockerfile --tag lemmy-federation:latest
+
+sudo docker-compose --file ../federation/docker-compose.yml --project-directory . up
--- /dev/null
+#!/bin/bash
+set -xe
+
+pushd ../../ui
+echo "Waiting for Lemmy to start..."
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8540/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8550/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8560/api/v1/site')" != "200" ]]; do sleep 1; done
+yarn api-test || true
+popd
USER root
RUN mkdir /app/dist/documentation/ -p \
&& addgroup --gid 1001 lemmy \
- && adduser --disabled-password --shell /bin/sh -u 1001 --ingroup lemmy lemmy
+ && adduser --gecos "" --disabled-password --shell /bin/sh -u 1001 --ingroup lemmy lemmy
# Copy resources
COPY server/config/defaults.hjson /app/config/defaults.hjson
- ../federation/nginx.conf:/etc/nginx/nginx.conf
restart: on-failure
depends_on:
- - lemmy_alpha
- - pictrs_alpha
- - lemmy_beta
- - pictrs_beta
- - lemmy_gamma
- - pictrs_gamma
+ - lemmy-alpha
+ - pictrs
+ - lemmy-beta
+ - lemmy-gamma
- iframely
- lemmy_alpha:
+ pictrs:
+ restart: always
+ image: asonix/pictrs:v0.1.13-r0
+ user: 991:991
+ volumes:
+ - ./volumes/pictrs_alpha:/mnt
+
+ lemmy-alpha:
image: lemmy-federation:latest
environment:
- - LEMMY_HOSTNAME=lemmy_alpha:8540
+ - LEMMY_HOSTNAME=lemmy-alpha:8540
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy_beta,lemmy_gamma
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma
- LEMMY_PORT=8540
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- - LEMMY_SETUP__SITE_NAME=lemmy_alpha
+ - LEMMY_SETUP__SITE_NAME=lemmy-alpha
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres_alpha:/var/lib/postgresql/data
- pictrs_alpha:
- image: asonix/pictrs:v0.1.13-r0
- user: 991:991
- volumes:
- - ./volumes/pictrs_alpha:/mnt
- lemmy_beta:
+ lemmy-beta:
image: lemmy-federation:latest
environment:
- - LEMMY_HOSTNAME=lemmy_beta:8550
+ - LEMMY_HOSTNAME=lemmy-beta:8550
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_beta:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy_alpha,lemmy_gamma
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma
- LEMMY_PORT=8550
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- - LEMMY_SETUP__SITE_NAME=lemmy_beta
+ - LEMMY_SETUP__SITE_NAME=lemmy-beta
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres_beta:/var/lib/postgresql/data
- pictrs_beta:
- image: asonix/pictrs:v0.1.13-r0
- user: 991:991
- volumes:
- - ./volumes/pictrs_beta:/mnt
- lemmy_gamma:
+ lemmy-gamma:
image: lemmy-federation:latest
environment:
- - LEMMY_HOSTNAME=lemmy_gamma:8560
+ - LEMMY_HOSTNAME=lemmy-gamma:8560
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_gamma:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy_alpha,lemmy_beta
+ - LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta
- LEMMY_PORT=8560
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- - LEMMY_SETUP__SITE_NAME=lemmy_gamma
+ - LEMMY_SETUP__SITE_NAME=lemmy-gamma
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres_gamma:/var/lib/postgresql/data
- pictrs_gamma:
- image: asonix/pictrs:v0.1.13-r0
- user: 991:991
- volumes:
- - ./volumes/pictrs_gamma:/mnt
iframely:
image: dogbin/iframely:latest
client_max_body_size 50M;
location / {
- proxy_pass http://lemmy_alpha:8540;
+ proxy_pass http://lemmy-alpha:8540;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# pict-rs images
location /pictrs {
location /pictrs/image {
- proxy_pass http://pictrs_alpha:8080/image;
+ proxy_pass http://pictrs:8080/image;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 50M;
location / {
- proxy_pass http://lemmy_beta:8550;
+ proxy_pass http://lemmy-beta:8550;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# pict-rs images
location /pictrs {
location /pictrs/image {
- proxy_pass http://pictrs_beta:8080/image;
+ proxy_pass http://pictrs:8080/image;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 50M;
location / {
- proxy_pass http://lemmy_gamma:8560;
+ proxy_pass http://lemmy-gamma:8560;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# pict-rs images
location /pictrs {
location /pictrs/image {
- proxy_pass http://pictrs_gamma:8080/image;
+ proxy_pass http://pictrs:8080/image;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
[[package]]
name = "actix"
-version = "0.9.0"
+version = "0.10.0-alpha.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4af87564ff659dee8f9981540cac9418c45e910c8072fdedd643a262a38fcaf"
+checksum = "a9028932f36d45df020c92317ccb879ab77d8f066f57ff143dd5bee93ba3de0d"
dependencies = [
- "actix-http",
"actix-rt",
"actix_derive",
"bitflags",
"bytes",
"crossbeam-channel",
"derive_more",
- "futures",
- "lazy_static",
+ "futures-channel",
+ "futures-util",
"log",
+ "once_cell",
"parking_lot",
"pin-project",
"smallvec",
"tokio",
- "tokio-util 0.2.0",
+ "tokio-util 0.3.1",
"trust-dns-proto",
"trust-dns-resolver",
]
[[package]]
name = "actix-connect"
-version = "1.0.2"
+version = "2.0.0-alpha.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c95cc9569221e9802bf4c377f6c18b90ef10227d787611decf79fd47d2a8e76c"
+checksum = "2551ed85d5e157c13f8f523cdb13a6292d948049eb2dc2072bbee3ec350399a2"
dependencies = [
"actix-codec",
"actix-rt",
"actix-utils",
"derive_more",
"either",
- "futures",
+ "futures-util",
"http",
"log",
+ "rustls",
+ "tokio-rustls",
"trust-dns-proto",
"trust-dns-resolver",
+ "webpki",
]
[[package]]
name = "actix-files"
-version = "0.2.2"
+version = "0.3.0-alpha.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "193b22cb1f7b4ff12a4eb2415d6d19e47e44ea93e05930b30d05375ea29d3529"
+checksum = "23b32e0fdd5998c2712549cbc39dff46c8754d55e3dd9f4d017d9e28de30cac6"
dependencies = [
"actix-http",
"actix-service",
[[package]]
name = "actix-http"
-version = "1.0.1"
+version = "2.0.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c16664cc4fdea8030837ad5a845eb231fb93fc3c5c171edfefb52fad92ce9019"
+checksum = "fd7ea0568480d199952a51de70271946da57c33cc0e8b83f54383e70958dff21"
dependencies = [
"actix-codec",
"actix-connect",
"actix-rt",
"actix-service",
"actix-threadpool",
+ "actix-tls",
"actix-utils",
- "base64 0.11.0",
+ "base64 0.12.3",
"bitflags",
"brotli2",
"bytes",
- "chrono",
"copyless",
"derive_more",
"either",
"encoding_rs",
- "failure",
"flate2",
"futures-channel",
"futures-core",
"serde 1.0.114",
"serde_json",
"serde_urlencoded",
- "sha1",
+ "sha-1",
"slab",
- "time",
+ "time 0.2.16",
]
[[package]]
[[package]]
name = "actix-tls"
-version = "1.0.0"
+version = "2.0.0-alpha.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4e5b4faaf105e9a6d389c606c298dcdb033061b00d532af9df56ff3a54995a8"
+checksum = "dd2d9f3e70cbad0f06c6922950c5997ba0fd44c82e143d1c374023eb50457588"
dependencies = [
"actix-codec",
"actix-rt",
"either",
"futures",
"log",
+ "rustls",
+ "tokio-rustls",
+ "webpki",
+ "webpki-roots",
]
[[package]]
[[package]]
name = "actix-web"
-version = "2.0.0"
+version = "3.0.0-alpha.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3158e822461040822f0dbf1735b9c2ce1f95f93b651d7a7aded00b1efbb1f635"
+checksum = "8bd6df56ec5f9a1a0d8335f156f36e1e8f76dbd736fa0cc0f6bc3a69be1e6124"
dependencies = [
"actix-codec",
"actix-http",
"bytes",
"derive_more",
"encoding_rs",
- "futures",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
"fxhash",
"log",
"mime",
- "net2",
"pin-project",
"regex",
+ "rustls",
"serde 1.0.114",
"serde_json",
"serde_urlencoded",
- "time",
+ "socket2",
+ "time 0.2.16",
+ "tinyvec",
"url",
]
[[package]]
name = "actix-web-actors"
-version = "2.0.0"
+version = "3.0.0-alpha.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1bd41bd66c4e9b5274cec87aac30168e63d64e96fd19db38edef6b46ba2982"
+checksum = "2b5efeb3907582f9c724ce27be093ab8aafabd97be828bc6750c0d467f5e1aa3"
dependencies = [
"actix",
"actix-codec",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
dependencies = [
"hermit-abi",
"libc",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
[[package]]
name = "awc"
-version = "1.0.1"
+version = "2.0.0-alpha.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5"
+checksum = "a7038a9747cd5159b9f0550895eaf865c0143baa7e4eee834e9294d0a7e0e4be"
dependencies = [
"actix-codec",
"actix-http",
"actix-rt",
"actix-service",
- "base64 0.11.0",
+ "base64 0.12.3",
"bytes",
"derive_more",
"futures-core",
"mime",
"percent-encoding",
"rand 0.7.3",
+ "rustls",
"serde 1.0.114",
"serde_json",
"serde_urlencoded",
"rustc-demangle",
]
+[[package]]
+name = "base-x"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
+
[[package]]
name = "base64"
version = "0.9.3"
[[package]]
name = "base64"
-version = "0.12.2"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67"
+checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
[[package]]
name = "bcrypt"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41b70db86f3c560199b0dada79a22b9a924622384abb2a756a9707ffcce077f2"
+checksum = "6378bd17c4830c1b7ed644dde88f247b1560d46c68ff3da1b788984b09c0df31"
dependencies = [
- "base64 0.12.2",
+ "base64 0.12.3",
"blowfish",
"byteorder",
"getrandom",
"block-padding",
"byte-tools",
"byteorder",
- "generic-array",
+ "generic-array 0.12.3",
]
[[package]]
-name = "block-cipher-trait"
-version = "0.6.2"
+name = "block-buffer"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
- "generic-array",
+ "generic-array 0.14.2",
+]
+
+[[package]]
+name = "block-cipher"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa136449e765dc7faa244561ccae839c394048667929af599b5d931ebe7b7f10"
+dependencies = [
+ "generic-array 0.14.2",
]
[[package]]
[[package]]
name = "blowfish"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3"
+checksum = "91d01392750dd899a2528948d6b856afe2df508d627fc7c339868c0bd0141b4b"
dependencies = [
- "block-cipher-trait",
+ "block-cipher",
"byteorder",
- "opaque-debug",
+ "opaque-debug 0.2.3",
]
[[package]]
[[package]]
name = "cc"
-version = "1.0.54"
+version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
+checksum = "b1be3409f94d7bdceeb5f5fac551039d9b3f00e25da7a74fc4d33400a0d96368"
[[package]]
name = "cfg-if"
"num-integer",
"num-traits 0.2.12",
"serde 1.0.114",
- "time",
+ "time 0.1.43",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
+[[package]]
+name = "cpuid-bool"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d375c433320f6c5057ae04a04376eef4d04ce2801448cf8863a78da99107be4"
+
[[package]]
name = "crc32fast"
version = "1.2.0"
"lazy_static",
]
-[[package]]
-name = "curl"
-version = "0.4.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0447a642435be046540f042950d874a4907f9fee28c0513a0beb3ba89f91eb7"
-dependencies = [
- "curl-sys",
- "libc",
- "openssl-probe",
- "openssl-sys",
- "schannel",
- "socket2",
- "winapi 0.3.8",
-]
-
-[[package]]
-name = "curl-sys"
-version = "0.4.32+curl-7.70.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "834425a2f22fdd621434196965bf99fbfd9eaed96348488e27b7ac40736c560b"
-dependencies = [
- "cc",
- "libc",
- "libnghttp2-sys",
- "libz-sys",
- "openssl-sys",
- "pkg-config",
- "vcpkg",
- "winapi 0.3.8",
-]
-
[[package]]
name = "darling"
version = "0.10.2"
[[package]]
name = "derive_more"
-version = "0.99.8"
+version = "0.99.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc655351f820d774679da6cdc23355a93de496867d8203496675162e17b1d671"
+checksum = "298998b1cf6b5b2c8a7b023dfd45821825ce3ba8a8af55c921a0e734e4653f76"
dependencies = [
"proc-macro2",
"quote",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
- "generic-array",
+ "generic-array 0.12.3",
]
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array 0.14.2",
+]
+
+[[package]]
+name = "discard"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
+
[[package]]
name = "dotenv"
version = "0.15.0"
"encoding",
"lazy_static",
"rand 0.4.6",
- "time",
+ "time 0.1.43",
"version_check 0.1.5",
]
"libc",
"log",
"rustc_version",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
"typenum",
]
+[[package]]
+name = "generic-array"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980"
+dependencies = [
+ "typenum",
+ "version_check 0.9.2",
+]
+
[[package]]
name = "getrandom"
version = "0.1.14"
dependencies = [
"libc",
"match_cfg",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
"thiserror",
]
+[[package]]
+name = "http-signature-normalization-actix"
+version = "0.4.0-alpha.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09afff6987c7edbed101d1cddd2185786fb0af0dd9c06b654aca73a0a763680f"
+dependencies = [
+ "actix-http",
+ "actix-web",
+ "base64 0.12.3",
+ "bytes",
+ "chrono",
+ "futures",
+ "http-signature-normalization",
+ "log",
+ "sha2",
+ "thiserror",
+]
+
[[package]]
name = "httparse"
version = "1.3.4"
dependencies = [
"socket2",
"widestring",
- "winapi 0.3.8",
+ "winapi 0.3.9",
"winreg",
]
-[[package]]
-name = "isahc"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f54e7cf252df9a36605ccfabea2a754ad30c24b51b77f830486e555ac8e76bce"
-dependencies = [
- "bytes",
- "crossbeam-channel",
- "crossbeam-utils",
- "curl",
- "curl-sys",
- "encoding_rs",
- "futures-channel",
- "futures-io",
- "futures-util",
- "http",
- "lazy_static",
- "log",
- "mime",
- "slab",
- "sluice",
- "tracing",
- "tracing-futures",
-]
-
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f325ae57ddcf609f02d891486ce740f5bbd0cc3e93f9bffaacdf6594b21404"
dependencies = [
- "base64 0.12.2",
+ "base64 0.12.3",
"pem",
"ring",
"serde 1.0.114",
"actix-rt",
"actix-web",
"actix-web-actors",
- "base64 0.12.2",
+ "async-trait",
+ "awc",
+ "base64 0.12.3",
"bcrypt",
"chrono",
"comrak",
"futures",
"htmlescape",
"http",
- "http-signature-normalization",
- "isahc",
+ "http-signature-normalization-actix",
"itertools",
"jsonwebtoken",
"lazy_static",
"email",
"lettre",
"mime",
- "time",
+ "time 0.1.43",
"uuid 0.7.4",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
-[[package]]
-name = "libnghttp2-sys"
-version = "0.1.4+1.41.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03624ec6df166e79e139a2310ca213283d6b3c30810c54844f307086d4488df1"
-dependencies = [
- "cc",
- "libc",
-]
-
-[[package]]
-name = "libz-sys"
-version = "1.0.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe"
-dependencies = [
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
-]
-
[[package]]
name = "linked-hash-map"
version = "0.3.0"
dependencies = [
"cfg-if",
"libc",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
[[package]]
name = "openssl"
-version = "0.10.29"
+version = "0.10.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
+checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59698ea79df9bf77104aefd39cc3ec990cb9693fb59c3b0a70ddf2646fdffb4b"
dependencies = [
- "base64 0.12.2",
+ "base64 0.12.3",
"once_cell",
"regex",
]
"libc",
"rand_core 0.3.1",
"rdrand",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
"rand_os",
"rand_pcg",
"rand_xorshift",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
dependencies = [
"libc",
"rand_core 0.4.2",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
"libc",
"rand_core 0.4.2",
"rdrand",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
"spin",
"untrusted",
"web-sys",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
"semver",
]
+[[package]]
+name = "rustls"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1"
+dependencies = [
+ "base64 0.11.0",
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
[[package]]
name = "ryu"
version = "1.0.5"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
dependencies = [
"lazy_static",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+[[package]]
+name = "sct"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
[[package]]
name = "security-framework"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
- "block-buffer",
- "digest",
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
"fake-simd",
- "opaque-debug",
+ "opaque-debug 0.2.3",
]
[[package]]
[[package]]
name = "sha2"
-version = "0.8.2"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
+checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
dependencies = [
- "block-buffer",
- "digest",
- "fake-simd",
- "opaque-debug",
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpuid-bool",
+ "digest 0.9.0",
+ "opaque-debug 0.3.0",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
-[[package]]
-name = "sluice"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed13b7cb46f13a15db2c4740f087a848acc8b31af89f95844d40137451f89b1"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-util",
-]
-
[[package]]
name = "smallvec"
version = "1.4.0"
"cfg-if",
"libc",
"redox_syscall",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+[[package]]
+name = "standback"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0437cfb83762844799a60e1e3b489d5ceb6a650fbacb86437badc1b6d87b246"
+dependencies = [
+ "version_check 0.9.2",
+]
+
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "stdweb"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
+dependencies = [
+ "discard",
+ "rustc_version",
+ "stdweb-derive",
+ "stdweb-internal-macros",
+ "stdweb-internal-runtime",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "stdweb-derive"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde 1.0.114",
+ "serde_derive",
+ "syn",
+]
+
+[[package]]
+name = "stdweb-internal-macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
+dependencies = [
+ "base-x",
+ "proc-macro2",
+ "quote",
+ "serde 1.0.114",
+ "serde_derive",
+ "serde_json",
+ "sha1",
+ "syn",
+]
+
+[[package]]
+name = "stdweb-internal-runtime"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
+
[[package]]
name = "strsim"
version = "0.8.0"
"rand 0.7.3",
"redox_syscall",
"remove_dir_all",
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
- "winapi 0.3.8",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "time"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a51cadc5b1eec673a685ff7c33192ff7b7603d0b75446fb354939ee615acb15"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "standback",
+ "stdweb",
+ "time-macros",
+ "version_check 0.9.2",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "time-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ae9b6e9f095bc105e183e3cd493d72579be3181ad4004fceb01adbe9eecab2d"
+dependencies = [
+ "proc-macro-hack",
+ "time-macros-impl",
+]
+
+[[package]]
+name = "time-macros-impl"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa"
+dependencies = [
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "standback",
+ "syn",
]
[[package]]
"pin-project-lite",
"signal-hook-registry",
"slab",
- "winapi 0.3.8",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4"
+dependencies = [
+ "futures-core",
+ "rustls",
+ "tokio",
+ "webpki",
]
[[package]]
dependencies = [
"bytes",
"futures-core",
+ "futures-io",
"futures-sink",
"log",
"pin-project-lite",
"tokio",
]
-[[package]]
-name = "tracing"
-version = "0.1.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a41f40ed0e162c911ac6fcb53ecdc8134c46905fdbbae8c50add462a538b495f"
-dependencies = [
- "cfg-if",
- "log",
- "tracing-attributes",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-attributes"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99bbad0de3fd923c9c3232ead88510b783e5a4d16a6154adffa3d53308de984c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "tracing-futures"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c"
-dependencies = [
- "pin-project",
- "tracing",
-]
-
[[package]]
name = "trust-dns-proto"
-version = "0.18.0-alpha.2"
+version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f"
+checksum = "cdd7061ba6f4d4d9721afedffbfd403f20f39a4301fee1b70d6fcd09cca69f28"
dependencies = [
"async-trait",
+ "backtrace",
"enum-as-inner",
- "failure",
"futures",
"idna",
"lazy_static",
"log",
"rand 0.7.3",
"smallvec",
- "socket2",
+ "thiserror",
"tokio",
"url",
]
[[package]]
name = "trust-dns-resolver"
-version = "0.18.0-alpha.2"
+version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f"
+checksum = "0f23cdfdc3d8300b3c50c9e84302d3bd6d860fb9529af84ace6cf9665f181b77"
dependencies = [
+ "backtrace",
"cfg-if",
- "failure",
"futures",
"ipconfig",
"lazy_static",
"lru-cache",
"resolv-conf",
"smallvec",
+ "thiserror",
"tokio",
"trust-dns-proto",
]
[[package]]
name = "unicode-xid"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "unicode_categories"
"wasm-bindgen",
]
+[[package]]
+name = "webpki"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739"
+dependencies = [
+ "webpki",
+]
+
[[package]]
name = "widestring"
version = "0.4.2"
[[package]]
name = "winapi"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
dependencies = [
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e"
dependencies = [
- "winapi 0.3.8",
+ "winapi 0.3.9",
]
[[package]]
serde_json = { version = "1.0.52", features = ["preserve_order"]}
failure = "0.1.8"
serde = { version = "1.0.105", features = ["derive"] }
-actix = "0.9.0"
-actix-web = "2.0.0"
-actix-files = "0.2.1"
-actix-web-actors = "2.0.0"
+actix = "0.10.0-alpha.2"
+actix-web = { version = "3.0.0-alpha.3", features = ["rustls"] }
+actix-files = "0.3.0-alpha.1"
+actix-web-actors = "3.0.0-alpha.1"
actix-rt = "1.1.1"
+awc = "2.0.0-alpha.2"
log = "0.4.0"
env_logger = "0.7.1"
rand = "0.7.3"
lazy_static = "1.3.0"
lettre = "0.9.3"
lettre_email = "0.9.4"
-sha2 = "0.8.1"
rss = "1.9.0"
htmlescape = "0.3.1"
url = { version = "2.1.1", features = ["serde"] }
config = {version = "0.10.1", default-features = false, features = ["hjson"] }
percent-encoding = "2.1.0"
-isahc = "0.9.2"
comrak = "0.7"
openssl = "0.10"
http = "0.2.1"
-http-signature-normalization = "0.5.1"
+http-signature-normalization-actix = { version = "0.4.0-alpha.0", default-features = false, features = ["sha-2"] }
base64 = "0.12.1"
tokio = "0.2.21"
futures = "0.3.5"
itertools = "0.9.0"
uuid = { version = "0.8", features = ["serde", "v4"] }
+sha2 = "0.9"
+async-trait = "0.1.36"
use crate::{
api::{APIError, Oper, Perform},
apub::{ApubLikeableType, ApubObjectType},
+ blocking,
db::{
comment::*,
comment_view::*,
UserOperation,
WebsocketInfo,
},
+ DbPool,
+ LemmyError,
MentionData,
};
-use diesel::{
- r2d2::{ConnectionManager, Pool},
- PgConnection,
-};
-use failure::Error;
use log::error;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
comments: Vec<CommentView>,
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<CreateComment> {
type Response = CommentResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<CommentResponse, Error> {
+ ) -> Result<CommentResponse, LemmyError> {
let data: &CreateComment = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- // Check for a community ban
- let post = Post::read(&conn, data.post_id)?;
- if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
- return Err(APIError::err("community_ban").into());
- }
-
- // Check for a site ban
- let user = User_::read(&conn, user_id)?;
- if user.banned {
- return Err(APIError::err("site_ban").into());
- }
-
let content_slurs_removed = remove_slurs(&data.content.to_owned());
let comment_form = CommentForm {
local: true,
};
- let inserted_comment = match Comment::create(&conn, &comment_form) {
- Ok(comment) => comment,
- Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
- };
+ // Check for a community ban
+ let post_id = data.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
- let updated_comment = match Comment::update_ap_id(&conn, inserted_comment.id) {
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(&conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ let comment_form2 = comment_form.clone();
+ let inserted_comment =
+ match blocking(pool, move |conn| Comment::create(&conn, &comment_form2)).await? {
+ Ok(comment) => comment,
+ Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
+ };
+
+ let inserted_comment_id = inserted_comment.id;
+ let updated_comment: Comment = match blocking(pool, move |conn| {
+ Comment::update_ap_id(&conn, inserted_comment_id)
+ })
+ .await?
+ {
Ok(comment) => comment,
Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
};
- updated_comment.send_create(&user, &conn)?;
+ updated_comment
+ .send_create(&user, &self.client, pool)
+ .await?;
// Scan the comment for user mentions, add those rows
let mentions = scrape_text_for_mentions(&comment_form.content);
- let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post);
+ let recipient_ids =
+ send_local_notifs(mentions, updated_comment.clone(), user.clone(), post, pool).await?;
// You like your own comment by default
let like_form = CommentLikeForm {
score: 1,
};
- let _inserted_like = match CommentLike::like(&conn, &like_form) {
- Ok(like) => like,
- Err(_e) => return Err(APIError::err("couldnt_like_comment").into()),
- };
+ let like = move |conn: &'_ _| CommentLike::like(&conn, &like_form);
+ if blocking(pool, like).await?.is_err() {
+ return Err(APIError::err("couldnt_like_comment").into());
+ }
- updated_comment.send_like(&user, &conn)?;
+ updated_comment.send_like(&user, &self.client, pool).await?;
- let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?;
+ let comment_view = blocking(pool, move |conn| {
+ CommentView::read(&conn, inserted_comment.id, Some(user_id))
+ })
+ .await??;
let mut res = CommentResponse {
comment: comment_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<EditComment> {
type Response = CommentResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<CommentResponse, Error> {
+ ) -> Result<CommentResponse, LemmyError> {
let data: &EditComment = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
+ let user = blocking(pool, move |conn| User_::read(&conn, user_id)).await??;
- let user = User_::read(&conn, user_id)?;
-
- let orig_comment = CommentView::read(&conn, data.edit_id, None)?;
+ let edit_id = data.edit_id;
+ let orig_comment =
+ blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// You are allowed to mark the comment as read even if you're banned.
if data.read.is_none() {
// Verify its the creator or a mod, or an admin
let mut editors: Vec<i32> = vec![data.creator_id];
+ let community_id = orig_comment.community_id;
+ editors.append(
+ &mut blocking(pool, move |conn| {
+ Ok(
+ CommunityModeratorView::for_community(&conn, community_id)?
+ .into_iter()
+ .map(|m| m.user_id)
+ .collect(),
+ ) as Result<_, LemmyError>
+ })
+ .await??,
+ );
editors.append(
- &mut CommunityModeratorView::for_community(&conn, orig_comment.community_id)?
- .into_iter()
- .map(|m| m.user_id)
- .collect(),
+ &mut blocking(pool, move |conn| {
+ Ok(UserView::admins(conn)?.into_iter().map(|a| a.id).collect()) as Result<_, LemmyError>
+ })
+ .await??,
);
- editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
if !editors.contains(&user_id) {
return Err(APIError::err("no_comment_edit_allowed").into());
}
// Check for a community ban
- if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() {
+ let community_id = orig_comment.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
let content_slurs_removed = remove_slurs(&data.content.to_owned());
- let read_comment = Comment::read(&conn, data.edit_id)?;
+ let edit_id = data.edit_id;
+ let read_comment = blocking(pool, move |conn| Comment::read(conn, edit_id)).await??;
let comment_form = CommentForm {
content: content_slurs_removed,
local: read_comment.local,
};
- let updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) {
+ let edit_id = data.edit_id;
+ let comment_form2 = comment_form.clone();
+ let updated_comment = match blocking(pool, move |conn| {
+ Comment::update(conn, edit_id, &comment_form2)
+ })
+ .await?
+ {
Ok(comment) => comment,
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
};
if let Some(deleted) = data.deleted.to_owned() {
if deleted {
- updated_comment.send_delete(&user, &conn)?;
+ updated_comment
+ .send_delete(&user, &self.client, pool)
+ .await?;
} else {
- updated_comment.send_undo_delete(&user, &conn)?;
+ updated_comment
+ .send_undo_delete(&user, &self.client, pool)
+ .await?;
}
} else if let Some(removed) = data.removed.to_owned() {
if removed {
- updated_comment.send_remove(&user, &conn)?;
+ updated_comment
+ .send_remove(&user, &self.client, pool)
+ .await?;
} else {
- updated_comment.send_undo_remove(&user, &conn)?;
+ updated_comment
+ .send_undo_remove(&user, &self.client, pool)
+ .await?;
}
} else {
- updated_comment.send_update(&user, &conn)?;
+ updated_comment
+ .send_update(&user, &self.client, pool)
+ .await?;
}
- let post = Post::read(&conn, data.post_id)?;
+ let post_id = data.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let mentions = scrape_text_for_mentions(&comment_form.content);
- let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post);
+ let recipient_ids = send_local_notifs(mentions, updated_comment, user, post, pool).await?;
// Mod tables
if let Some(removed) = data.removed.to_owned() {
removed: Some(removed),
reason: data.reason.to_owned(),
};
- ModRemoveComment::create(&conn, &form)?;
+ blocking(pool, move |conn| ModRemoveComment::create(conn, &form)).await??;
}
- let comment_view = CommentView::read(&conn, data.edit_id, Some(user_id))?;
+ let edit_id = data.edit_id;
+ let comment_view = blocking(pool, move |conn| {
+ CommentView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
let mut res = CommentResponse {
comment: comment_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<SaveComment> {
type Response = CommentResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<CommentResponse, Error> {
+ ) -> Result<CommentResponse, LemmyError> {
let data: &SaveComment = &self.data;
let claims = match Claims::decode(&data.auth) {
user_id,
};
- let conn = pool.get()?;
-
if data.save {
- match CommentSaved::save(&conn, &comment_saved_form) {
- Ok(comment) => comment,
- Err(_e) => return Err(APIError::err("couldnt_save_comment").into()),
- };
+ let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
+ if blocking(pool, save_comment).await?.is_err() {
+ return Err(APIError::err("couldnt_save_comment").into());
+ }
} else {
- match CommentSaved::unsave(&conn, &comment_saved_form) {
- Ok(comment) => comment,
- Err(_e) => return Err(APIError::err("couldnt_save_comment").into()),
- };
+ let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
+ if blocking(pool, unsave_comment).await?.is_err() {
+ return Err(APIError::err("couldnt_save_comment").into());
+ }
}
- let comment_view = CommentView::read(&conn, data.comment_id, Some(user_id))?;
+ let comment_id = data.comment_id;
+ let comment_view = blocking(pool, move |conn| {
+ CommentView::read(conn, comment_id, Some(user_id))
+ })
+ .await??;
Ok(CommentResponse {
comment: comment_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<CreateCommentLike> {
type Response = CommentResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<CommentResponse, Error> {
+ ) -> Result<CommentResponse, LemmyError> {
let data: &CreateCommentLike = &self.data;
let claims = match Claims::decode(&data.auth) {
let mut recipient_ids = Vec::new();
- let conn = pool.get()?;
-
// Don't do a downvote if site has downvotes disabled
if data.score == -1 {
- let site = SiteView::read(&conn)?;
+ let site = blocking(pool, move |conn| SiteView::read(conn)).await??;
if !site.enable_downvotes {
return Err(APIError::err("downvotes_disabled").into());
}
}
// Check for a community ban
- let post = Post::read(&conn, data.post_id)?;
- if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
+ let post_id = data.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+ let community_id = post.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
- let comment = Comment::read(&conn, data.comment_id)?;
+ let comment_id = data.comment_id;
+ let comment = blocking(pool, move |conn| Comment::read(conn, comment_id)).await??;
// Add to recipient ids
match comment.parent_id {
Some(parent_id) => {
- let parent_comment = Comment::read(&conn, parent_id)?;
+ let parent_comment = blocking(pool, move |conn| Comment::read(conn, parent_id)).await??;
if parent_comment.creator_id != user_id {
- let parent_user = User_::read(&conn, parent_comment.creator_id)?;
+ let parent_user = blocking(pool, move |conn| {
+ User_::read(conn, parent_comment.creator_id)
+ })
+ .await??;
recipient_ids.push(parent_user.id);
}
}
};
// Remove any likes first
- CommentLike::remove(&conn, &like_form)?;
+ let like_form2 = like_form.clone();
+ blocking(pool, move |conn| CommentLike::remove(conn, &like_form2)).await??;
// Only add the like if the score isnt 0
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
if do_add {
- let _inserted_like = match CommentLike::like(&conn, &like_form) {
- Ok(like) => like,
- Err(_e) => return Err(APIError::err("couldnt_like_comment").into()),
- };
+ let like_form2 = like_form.clone();
+ let like = move |conn: &'_ _| CommentLike::like(conn, &like_form2);
+ if blocking(pool, like).await?.is_err() {
+ return Err(APIError::err("couldnt_like_comment").into());
+ }
if like_form.score == 1 {
- comment.send_like(&user, &conn)?;
+ comment.send_like(&user, &self.client, pool).await?;
} else if like_form.score == -1 {
- comment.send_dislike(&user, &conn)?;
+ comment.send_dislike(&user, &self.client, pool).await?;
}
} else {
- comment.send_undo_like(&user, &conn)?;
+ comment.send_undo_like(&user, &self.client, pool).await?;
}
// Have to refetch the comment to get the current state
- let liked_comment = CommentView::read(&conn, data.comment_id, Some(user_id))?;
+ let comment_id = data.comment_id;
+ let liked_comment = blocking(pool, move |conn| {
+ CommentView::read(conn, comment_id, Some(user_id))
+ })
+ .await??;
let mut res = CommentResponse {
comment: liked_comment,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetComments> {
type Response = GetCommentsResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetCommentsResponse, Error> {
+ ) -> Result<GetCommentsResponse, LemmyError> {
let data: &GetComments = &self.data;
let user_claims: Option<Claims> = match &data.auth {
let type_ = ListingType::from_str(&data.type_)?;
let sort = SortType::from_str(&data.sort)?;
- let conn = pool.get()?;
-
- let comments = match CommentQueryBuilder::create(&conn)
- .listing_type(type_)
- .sort(&sort)
- .for_community_id(data.community_id)
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit)
- .list()
- {
+ let community_id = data.community_id;
+ let page = data.page;
+ let limit = data.limit;
+ let comments = blocking(pool, move |conn| {
+ CommentQueryBuilder::create(conn)
+ .listing_type(type_)
+ .sort(&sort)
+ .for_community_id(community_id)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await?;
+ let comments = match comments {
Ok(comments) => comments,
- Err(_e) => return Err(APIError::err("couldnt_get_comments").into()),
+ Err(_) => return Err(APIError::err("couldnt_get_comments").into()),
};
if let Some(ws) = websocket_info {
}
}
-pub fn send_local_notifs(
- conn: &PgConnection,
+pub async fn send_local_notifs(
+ mentions: Vec<MentionData>,
+ comment: Comment,
+ user: User_,
+ post: Post,
+ pool: &DbPool,
+) -> Result<Vec<i32>, LemmyError> {
+ let ids = blocking(pool, move |conn| {
+ do_send_local_notifs(conn, &mentions, &comment, &user, &post)
+ })
+ .await?;
+
+ Ok(ids)
+}
+
+fn do_send_local_notifs(
+ conn: &diesel::PgConnection,
mentions: &[MentionData],
comment: &Comment,
user: &User_,
ActorType,
EndpointType,
},
+ blocking,
db::{Bannable, Crud, Followable, Joinable, SortType},
is_valid_community_name,
naive_from_unix,
UserOperation,
WebsocketInfo,
},
+ DbPool,
+ LemmyError,
};
-use diesel::{
- r2d2::{ConnectionManager, Pool},
- PgConnection,
-};
-use failure::Error;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
auth: String,
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetCommunity> {
type Response = GetCommunityResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetCommunityResponse, Error> {
+ ) -> Result<GetCommunityResponse, LemmyError> {
let data: &GetCommunity = &self.data;
let user_id: Option<i32> = match &data.auth {
None => None,
};
- let conn = pool.get()?;
-
+ let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
let community = match data.id {
- Some(id) => Community::read(&conn, id)?,
- None => {
- match Community::read_from_name(
- &conn,
- &data.name.to_owned().unwrap_or_else(|| "main".to_string()),
- ) {
- Ok(community) => community,
- Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
- }
- }
+ Some(id) => blocking(pool, move |conn| Community::read(conn, id)).await??,
+ None => match blocking(pool, move |conn| Community::read_from_name(conn, &name)).await? {
+ Ok(community) => community,
+ Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
+ },
};
- let community_view = match CommunityView::read(&conn, community.id, user_id) {
+ let community_id = community.id;
+ let community_view = match blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, user_id)
+ })
+ .await?
+ {
Ok(community) => community,
Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
};
- let moderators = match CommunityModeratorView::for_community(&conn, community.id) {
+ let community_id = community.id;
+ let moderators: Vec<CommunityModeratorView> = match blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await?
+ {
Ok(moderators) => moderators,
Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
};
- let site_creator_id = Site::read(&conn, 1)?.creator_id;
- let mut admins = UserView::admins(&conn)?;
+ let site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
+ let site_creator_id = site.creator_id;
+ let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<CreateCommunity> {
type Response = CommunityResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<CommunityResponse, Error> {
+ ) -> Result<CommunityResponse, LemmyError> {
let data: &CreateCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Check for a site ban
- if UserView::read(&conn, user_id)?.banned {
+ let user_view = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
+ if user_view.banned {
return Err(APIError::err("site_ban").into());
}
published: None,
};
- let inserted_community = match Community::create(&conn, &community_form) {
- Ok(community) => community,
- Err(_e) => return Err(APIError::err("community_already_exists").into()),
- };
+ let inserted_community =
+ match blocking(pool, move |conn| Community::create(conn, &community_form)).await? {
+ Ok(community) => community,
+ Err(_e) => return Err(APIError::err("community_already_exists").into()),
+ };
let community_moderator_form = CommunityModeratorForm {
community_id: inserted_community.id,
user_id,
};
- let _inserted_community_moderator =
- match CommunityModerator::join(&conn, &community_moderator_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
- };
+ let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
+ if blocking(pool, join).await?.is_err() {
+ return Err(APIError::err("community_moderator_already_exists").into());
+ }
let community_follower_form = CommunityFollowerForm {
community_id: inserted_community.id,
user_id,
};
- let _inserted_community_follower =
- match CommunityFollower::follow(&conn, &community_follower_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
- };
+ let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
+ if blocking(pool, follow).await?.is_err() {
+ return Err(APIError::err("community_follower_already_exists").into());
+ }
- let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
+ let community_view = blocking(pool, move |conn| {
+ CommunityView::read(conn, inserted_community.id, Some(user_id))
+ })
+ .await??;
Ok(CommunityResponse {
community: community_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<EditCommunity> {
type Response = CommunityResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<CommunityResponse, Error> {
+ ) -> Result<CommunityResponse, LemmyError> {
let data: &EditCommunity = &self.data;
if let Err(slurs) = slur_check(&data.name) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Check for a site ban
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Verify its a mod
+ let edit_id = data.edit_id;
let mut editors: Vec<i32> = Vec::new();
editors.append(
- &mut CommunityModeratorView::for_community(&conn, data.edit_id)?
- .into_iter()
- .map(|m| m.user_id)
- .collect(),
+ &mut blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, edit_id)
+ .map(|v| v.into_iter().map(|m| m.user_id).collect())
+ })
+ .await??,
+ );
+ editors.append(
+ &mut blocking(pool, move |conn| {
+ UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
+ })
+ .await??,
);
- editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
if !editors.contains(&user_id) {
return Err(APIError::err("no_community_edit_allowed").into());
}
- let read_community = Community::read(&conn, data.edit_id)?;
+ let edit_id = data.edit_id;
+ let read_community = blocking(pool, move |conn| Community::read(conn, edit_id)).await??;
let community_form = CommunityForm {
name: data.name.to_owned(),
published: None,
};
- let updated_community = match Community::update(&conn, data.edit_id, &community_form) {
+ let edit_id = data.edit_id;
+ let updated_community = match blocking(pool, move |conn| {
+ Community::update(conn, edit_id, &community_form)
+ })
+ .await?
+ {
Ok(community) => community,
Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
};
reason: data.reason.to_owned(),
expires,
};
- ModRemoveCommunity::create(&conn, &form)?;
+ blocking(pool, move |conn| ModRemoveCommunity::create(conn, &form)).await??;
}
if let Some(deleted) = data.deleted.to_owned() {
if deleted {
- updated_community.send_delete(&user, &conn)?;
+ updated_community
+ .send_delete(&user, &self.client, pool)
+ .await?;
} else {
- updated_community.send_undo_delete(&user, &conn)?;
+ updated_community
+ .send_undo_delete(&user, &self.client, pool)
+ .await?;
}
} else if let Some(removed) = data.removed.to_owned() {
if removed {
- updated_community.send_remove(&user, &conn)?;
+ updated_community
+ .send_remove(&user, &self.client, pool)
+ .await?;
} else {
- updated_community.send_undo_remove(&user, &conn)?;
+ updated_community
+ .send_undo_remove(&user, &self.client, pool)
+ .await?;
}
}
- let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
+ let edit_id = data.edit_id;
+ let community_view = blocking(pool, move |conn| {
+ CommunityView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
let res = CommunityResponse {
community: community_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<ListCommunities> {
type Response = ListCommunitiesResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<ListCommunitiesResponse, Error> {
+ ) -> Result<ListCommunitiesResponse, LemmyError> {
let data: &ListCommunities = &self.data;
let user_claims: Option<Claims> = match &data.auth {
let sort = SortType::from_str(&data.sort)?;
- let conn = pool.get()?;
-
- let communities = CommunityQueryBuilder::create(&conn)
- .sort(&sort)
- .for_user(user_id)
- .show_nsfw(show_nsfw)
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ let page = data.page;
+ let limit = data.limit;
+ let communities = blocking(pool, move |conn| {
+ CommunityQueryBuilder::create(conn)
+ .sort(&sort)
+ .for_user(user_id)
+ .show_nsfw(show_nsfw)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
// Return the jwt
Ok(ListCommunitiesResponse { communities })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<FollowCommunity> {
type Response = CommunityResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<CommunityResponse, Error> {
+ ) -> Result<CommunityResponse, LemmyError> {
let data: &FollowCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let community = Community::read(&conn, data.community_id)?;
+ let community_id = data.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
let community_follower_form = CommunityFollowerForm {
community_id: data.community_id,
user_id,
if community.local {
if data.follow {
- match CommunityFollower::follow(&conn, &community_follower_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
- };
+ let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
+ if blocking(pool, follow).await?.is_err() {
+ return Err(APIError::err("community_follower_already_exists").into());
+ }
} else {
- match CommunityFollower::unfollow(&conn, &community_follower_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
- };
+ let unfollow =
+ move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
+ if blocking(pool, unfollow).await?.is_err() {
+ return Err(APIError::err("community_follower_already_exists").into());
+ }
}
} else {
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if data.follow {
// Dont actually add to the community followers here, because you need
// to wait for the accept
- user.send_follow(&community.actor_id, &conn)?;
+ user
+ .send_follow(&community.actor_id, &self.client, pool)
+ .await?;
} else {
- user.send_unfollow(&community.actor_id, &conn)?;
- match CommunityFollower::unfollow(&conn, &community_follower_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
- };
+ user
+ .send_unfollow(&community.actor_id, &self.client, pool)
+ .await?;
+ let unfollow =
+ move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
+ if blocking(pool, unfollow).await?.is_err() {
+ return Err(APIError::err("community_follower_already_exists").into());
+ }
}
// TODO: this needs to return a "pending" state, until Accept is received from the remote server
}
- let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
+ let community_id = data.community_id;
+ let community_view = blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, Some(user_id))
+ })
+ .await??;
Ok(CommunityResponse {
community: community_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetFollowedCommunities> {
type Response = GetFollowedCommunitiesResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetFollowedCommunitiesResponse, Error> {
+ ) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
let data: &GetFollowedCommunities = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let communities: Vec<CommunityFollowerView> =
- match CommunityFollowerView::for_user(&conn, user_id) {
- Ok(communities) => communities,
- Err(_e) => return Err(APIError::err("system_err_login").into()),
- };
+ let communities = match blocking(pool, move |conn| {
+ CommunityFollowerView::for_user(conn, user_id)
+ })
+ .await?
+ {
+ Ok(communities) => communities,
+ _ => return Err(APIError::err("system_err_login").into()),
+ };
// Return the jwt
Ok(GetFollowedCommunitiesResponse { communities })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<BanFromCommunity> {
type Response = BanFromCommunityResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<BanFromCommunityResponse, Error> {
+ ) -> Result<BanFromCommunityResponse, LemmyError> {
let data: &BanFromCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
user_id: data.user_id,
};
- let conn = pool.get()?;
-
if data.ban {
- match CommunityUserBan::ban(&conn, &community_user_ban_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
- };
+ let ban = move |conn: &'_ _| CommunityUserBan::ban(conn, &community_user_ban_form);
+ if blocking(pool, ban).await?.is_err() {
+ return Err(APIError::err("community_user_already_banned").into());
+ }
} else {
- match CommunityUserBan::unban(&conn, &community_user_ban_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
- };
+ let unban = move |conn: &'_ _| CommunityUserBan::unban(conn, &community_user_ban_form);
+ if blocking(pool, unban).await?.is_err() {
+ return Err(APIError::err("community_user_already_banned").into());
+ }
}
// Mod tables
banned: Some(data.ban),
expires,
};
- ModBanFromCommunity::create(&conn, &form)?;
+ blocking(pool, move |conn| ModBanFromCommunity::create(conn, &form)).await??;
- let user_view = UserView::read(&conn, data.user_id)?;
+ let user_id = data.user_id;
+ let user_view = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
let res = BanFromCommunityResponse {
user: user_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<AddModToCommunity> {
type Response = AddModToCommunityResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<AddModToCommunityResponse, Error> {
+ ) -> Result<AddModToCommunityResponse, LemmyError> {
let data: &AddModToCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
user_id: data.user_id,
};
- let conn = pool.get()?;
-
if data.added {
- match CommunityModerator::join(&conn, &community_moderator_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
- };
+ let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
+ if blocking(pool, join).await?.is_err() {
+ return Err(APIError::err("community_moderator_already_exists").into());
+ }
} else {
- match CommunityModerator::leave(&conn, &community_moderator_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
- };
+ let leave = move |conn: &'_ _| CommunityModerator::leave(conn, &community_moderator_form);
+ if blocking(pool, leave).await?.is_err() {
+ return Err(APIError::err("community_moderator_already_exists").into());
+ }
}
// Mod tables
community_id: data.community_id,
removed: Some(!data.added),
};
- ModAddCommunity::create(&conn, &form)?;
+ blocking(pool, move |conn| ModAddCommunity::create(conn, &form)).await??;
- let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
+ let community_id = data.community_id;
+ let moderators = blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await??;
let res = AddModToCommunityResponse { moderators };
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<TransferCommunity> {
type Response = GetCommunityResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetCommunityResponse, Error> {
+ ) -> Result<GetCommunityResponse, LemmyError> {
let data: &TransferCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
+ let community_id = data.community_id;
+ let read_community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
+ let site_creator_id =
+ blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
- let read_community = Community::read(&conn, data.community_id)?;
+ let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
- let site_creator_id = Site::read(&conn, 1)?.creator_id;
- let mut admins = UserView::admins(&conn)?;
let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
published: None,
};
- let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
- Ok(community) => community,
- Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
+ let community_id = data.community_id;
+ let update = move |conn: &'_ _| Community::update(conn, community_id, &community_form);
+ if blocking(pool, update).await?.is_err() {
+ return Err(APIError::err("couldnt_update_community").into());
};
// You also have to re-do the community_moderator table, reordering it.
- let mut community_mods = CommunityModeratorView::for_community(&conn, data.community_id)?;
+ let community_id = data.community_id;
+ let mut community_mods = blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await??;
let creator_index = community_mods
.iter()
.position(|r| r.user_id == data.user_id)
let creator_user = community_mods.remove(creator_index);
community_mods.insert(0, creator_user);
- CommunityModerator::delete_for_community(&conn, data.community_id)?;
+ let community_id = data.community_id;
+ blocking(pool, move |conn| {
+ CommunityModerator::delete_for_community(conn, community_id)
+ })
+ .await??;
+ // TODO: this should probably be a bulk operation
for cmod in &community_mods {
let community_moderator_form = CommunityModeratorForm {
community_id: cmod.community_id,
user_id: cmod.user_id,
};
- let _inserted_community_moderator =
- match CommunityModerator::join(&conn, &community_moderator_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
- };
+ let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
+ if blocking(pool, join).await?.is_err() {
+ return Err(APIError::err("community_moderator_already_exists").into());
+ }
}
// Mod tables
community_id: data.community_id,
removed: Some(false),
};
- ModAddCommunity::create(&conn, &form)?;
+ blocking(pool, move |conn| ModAddCommunity::create(conn, &form)).await??;
- let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
+ let community_id = data.community_id;
+ let community_view = match blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, Some(user_id))
+ })
+ .await?
+ {
Ok(community) => community,
Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
};
- let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
+ let community_id = data.community_id;
+ let moderators = match blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await?
+ {
Ok(moderators) => moderators,
Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
};
use crate::{
db::{community::*, community_view::*, moderator::*, site::*, user::*, user_view::*},
websocket::WebsocketInfo,
+ DbPool,
+ LemmyError,
};
-use diesel::{
- r2d2::{ConnectionManager, Pool},
- PgConnection,
-};
-use failure::Error;
+use actix_web::client::Client;
pub mod comment;
pub mod community;
pub struct Oper<T> {
data: T,
+ client: Client,
}
impl<Data> Oper<Data> {
- pub fn new(data: Data) -> Oper<Data> {
- Oper { data }
+ pub fn new(data: Data, client: Client) -> Oper<Data> {
+ Oper { data, client }
}
}
+#[async_trait::async_trait(?Send)]
pub trait Perform {
type Response: serde::ser::Serialize + Send;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<Self::Response, Error>;
+ ) -> Result<Self::Response, LemmyError>;
}
use crate::{
api::{APIError, Oper, Perform},
apub::{ApubLikeableType, ApubObjectType},
+ blocking,
db::{
comment_view::*,
community_view::*,
UserOperation,
WebsocketInfo,
},
+ DbPool,
+ LemmyError,
};
-use diesel::{
- r2d2::{ConnectionManager, Pool},
- PgConnection,
-};
-use failure::Error;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
auth: String,
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<CreatePost> {
type Response = PostResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<PostResponse, Error> {
+ ) -> Result<PostResponse, LemmyError> {
let data: &CreatePost = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Check for a community ban
- if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
+ let community_id = data.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Fetch Iframely and pictrs cached image
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
- fetch_iframely_and_pictrs_data(data.url.to_owned());
+ fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await;
let post_form = PostForm {
name: data.name.to_owned(),
published: None,
};
- let inserted_post = match Post::create(&conn, &post_form) {
+ let inserted_post = match blocking(pool, move |conn| Post::create(conn, &post_form)).await? {
Ok(post) => post,
Err(e) => {
let err_type = if e.to_string() == "value too long for type character varying(200)" {
}
};
- let updated_post = match Post::update_ap_id(&conn, inserted_post.id) {
- Ok(post) => post,
- Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
- };
+ let inserted_post_id = inserted_post.id;
+ let updated_post =
+ match blocking(pool, move |conn| Post::update_ap_id(conn, inserted_post_id)).await? {
+ Ok(post) => post,
+ Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
+ };
- updated_post.send_create(&user, &conn)?;
+ updated_post.send_create(&user, &self.client, pool).await?;
// They like their own post by default
let like_form = PostLikeForm {
score: 1,
};
- let _inserted_like = match PostLike::like(&conn, &like_form) {
- Ok(like) => like,
- Err(_e) => return Err(APIError::err("couldnt_like_post").into()),
- };
+ let like = move |conn: &'_ _| PostLike::like(conn, &like_form);
+ if blocking(pool, like).await?.is_err() {
+ return Err(APIError::err("couldnt_like_post").into());
+ }
- updated_post.send_like(&user, &conn)?;
+ updated_post.send_like(&user, &self.client, pool).await?;
// Refetch the view
- let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) {
+ let inserted_post_id = inserted_post.id;
+ let post_view = match blocking(pool, move |conn| {
+ PostView::read(conn, inserted_post_id, Some(user_id))
+ })
+ .await?
+ {
Ok(post) => post,
Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
};
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetPost> {
type Response = GetPostResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetPostResponse, Error> {
+ ) -> Result<GetPostResponse, LemmyError> {
let data: &GetPost = &self.data;
let user_id: Option<i32> = match &data.auth {
None => None,
};
- let conn = pool.get()?;
-
- let post_view = match PostView::read(&conn, data.id, user_id) {
+ let id = data.id;
+ let post_view = match blocking(pool, move |conn| PostView::read(conn, id, user_id)).await? {
Ok(post) => post,
Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
};
- let comments = CommentQueryBuilder::create(&conn)
- .for_post_id(data.id)
- .my_user_id(user_id)
- .limit(9999)
- .list()?;
+ let id = data.id;
+ let comments = blocking(pool, move |conn| {
+ CommentQueryBuilder::create(conn)
+ .for_post_id(id)
+ .my_user_id(user_id)
+ .limit(9999)
+ .list()
+ })
+ .await??;
+
+ let community_id = post_view.community_id;
+ let community = blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, user_id)
+ })
+ .await??;
- let community = CommunityView::read(&conn, post_view.community_id, user_id)?;
+ let community_id = post_view.community_id;
+ let moderators = blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await??;
- let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id)?;
+ let site_creator_id =
+ blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
- let site_creator_id = Site::read(&conn, 1)?.creator_id;
- let mut admins = UserView::admins(&conn)?;
+ let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetPosts> {
type Response = GetPostsResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetPostsResponse, Error> {
+ ) -> Result<GetPostsResponse, LemmyError> {
let data: &GetPosts = &self.data;
let user_claims: Option<Claims> = match &data.auth {
let type_ = ListingType::from_str(&data.type_)?;
let sort = SortType::from_str(&data.sort)?;
- let conn = pool.get()?;
-
- let posts = match PostQueryBuilder::create(&conn)
- .listing_type(type_)
- .sort(&sort)
- .show_nsfw(show_nsfw)
- .for_community_id(data.community_id)
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit)
- .list()
+ let page = data.page;
+ let limit = data.limit;
+ let community_id = data.community_id;
+ let posts = match blocking(pool, move |conn| {
+ PostQueryBuilder::create(conn)
+ .listing_type(type_)
+ .sort(&sort)
+ .show_nsfw(show_nsfw)
+ .for_community_id(community_id)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await?
{
Ok(posts) => posts,
Err(_e) => return Err(APIError::err("couldnt_get_posts").into()),
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<CreatePostLike> {
type Response = PostResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<PostResponse, Error> {
+ ) -> Result<PostResponse, LemmyError> {
let data: &CreatePostLike = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Don't do a downvote if site has downvotes disabled
if data.score == -1 {
- let site = SiteView::read(&conn)?;
+ let site = blocking(pool, move |conn| SiteView::read(conn)).await??;
if !site.enable_downvotes {
return Err(APIError::err("downvotes_disabled").into());
}
}
// Check for a community ban
- let post = Post::read(&conn, data.post_id)?;
- if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
+ let post_id = data.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
};
// Remove any likes first
- PostLike::remove(&conn, &like_form)?;
+ let like_form2 = like_form.clone();
+ blocking(pool, move |conn| PostLike::remove(conn, &like_form2)).await??;
// Only add the like if the score isnt 0
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
if do_add {
- let _inserted_like = match PostLike::like(&conn, &like_form) {
- Ok(like) => like,
- Err(_e) => return Err(APIError::err("couldnt_like_post").into()),
- };
+ let like_form2 = like_form.clone();
+ let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
+ if blocking(pool, like).await?.is_err() {
+ return Err(APIError::err("couldnt_like_post").into());
+ }
if like_form.score == 1 {
- post.send_like(&user, &conn)?;
+ post.send_like(&user, &self.client, pool).await?;
} else if like_form.score == -1 {
- post.send_dislike(&user, &conn)?;
+ post.send_dislike(&user, &self.client, pool).await?;
}
} else {
- post.send_undo_like(&user, &conn)?;
+ post.send_undo_like(&user, &self.client, pool).await?;
}
- let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) {
+ let post_id = data.post_id;
+ let post_view = match blocking(pool, move |conn| {
+ PostView::read(conn, post_id, Some(user_id))
+ })
+ .await?
+ {
Ok(post) => post,
Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
};
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<EditPost> {
type Response = PostResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<PostResponse, Error> {
+ ) -> Result<PostResponse, LemmyError> {
let data: &EditPost = &self.data;
if let Err(slurs) = slur_check(&data.name) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Verify its the creator or a mod or admin
+ let community_id = data.community_id;
let mut editors: Vec<i32> = vec![data.creator_id];
editors.append(
- &mut CommunityModeratorView::for_community(&conn, data.community_id)?
- .into_iter()
- .map(|m| m.user_id)
- .collect(),
+ &mut blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ .map(|v| v.into_iter().map(|m| m.user_id).collect())
+ })
+ .await??,
+ );
+ editors.append(
+ &mut blocking(pool, move |conn| {
+ UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
+ })
+ .await??,
);
- editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
if !editors.contains(&user_id) {
return Err(APIError::err("no_post_edit_allowed").into());
}
// Check for a community ban
- if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
+ let community_id = data.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Fetch Iframely and Pictrs cached image
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
- fetch_iframely_and_pictrs_data(data.url.to_owned());
+ fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await;
- let read_post = Post::read(&conn, data.edit_id)?;
+ let edit_id = data.edit_id;
+ let read_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
let post_form = PostForm {
name: data.name.to_owned(),
published: None,
};
- let updated_post = match Post::update(&conn, data.edit_id, &post_form) {
+ let edit_id = data.edit_id;
+ let res = blocking(pool, move |conn| Post::update(conn, edit_id, &post_form)).await?;
+ let updated_post: Post = match res {
Ok(post) => post,
Err(e) => {
let err_type = if e.to_string() == "value too long for type character varying(200)" {
removed: Some(removed),
reason: data.reason.to_owned(),
};
- ModRemovePost::create(&conn, &form)?;
+ blocking(pool, move |conn| ModRemovePost::create(conn, &form)).await??;
}
if let Some(locked) = data.locked.to_owned() {
post_id: data.edit_id,
locked: Some(locked),
};
- ModLockPost::create(&conn, &form)?;
+ blocking(pool, move |conn| ModLockPost::create(conn, &form)).await??;
}
if let Some(stickied) = data.stickied.to_owned() {
post_id: data.edit_id,
stickied: Some(stickied),
};
- ModStickyPost::create(&conn, &form)?;
+ blocking(pool, move |conn| ModStickyPost::create(conn, &form)).await??;
}
if let Some(deleted) = data.deleted.to_owned() {
if deleted {
- updated_post.send_delete(&user, &conn)?;
+ updated_post.send_delete(&user, &self.client, pool).await?;
} else {
- updated_post.send_undo_delete(&user, &conn)?;
+ updated_post
+ .send_undo_delete(&user, &self.client, pool)
+ .await?;
}
} else if let Some(removed) = data.removed.to_owned() {
if removed {
- updated_post.send_remove(&user, &conn)?;
+ updated_post.send_remove(&user, &self.client, pool).await?;
} else {
- updated_post.send_undo_remove(&user, &conn)?;
+ updated_post
+ .send_undo_remove(&user, &self.client, pool)
+ .await?;
}
} else {
- updated_post.send_update(&user, &conn)?;
+ updated_post.send_update(&user, &self.client, pool).await?;
}
- let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
+ let edit_id = data.edit_id;
+ let post_view = blocking(pool, move |conn| {
+ PostView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
let res = PostResponse { post: post_view };
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<SavePost> {
type Response = PostResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<PostResponse, Error> {
+ ) -> Result<PostResponse, LemmyError> {
let data: &SavePost = &self.data;
let claims = match Claims::decode(&data.auth) {
user_id,
};
- let conn = pool.get()?;
-
if data.save {
- match PostSaved::save(&conn, &post_saved_form) {
- Ok(post) => post,
- Err(_e) => return Err(APIError::err("couldnt_save_post").into()),
- };
+ let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
+ if blocking(pool, save).await?.is_err() {
+ return Err(APIError::err("couldnt_save_post").into());
+ }
} else {
- match PostSaved::unsave(&conn, &post_saved_form) {
- Ok(post) => post,
- Err(_e) => return Err(APIError::err("couldnt_save_post").into()),
- };
+ let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
+ if blocking(pool, unsave).await?.is_err() {
+ return Err(APIError::err("couldnt_save_post").into());
+ }
}
- let post_view = PostView::read(&conn, data.post_id, Some(user_id))?;
+ let post_id = data.post_id;
+ let post_view = blocking(pool, move |conn| {
+ PostView::read(conn, post_id, Some(user_id))
+ })
+ .await??;
Ok(PostResponse { post: post_view })
}
use crate::{
api::{APIError, Oper, Perform},
apub::fetcher::search_by_apub_id,
+ blocking,
db::{
category::*,
comment_view::*,
slur_check,
slurs_vec_to_str,
websocket::{server::SendAllMessage, UserOperation, WebsocketInfo},
+ DbPool,
+ LemmyError,
};
-use diesel::{
- r2d2::{ConnectionManager, Pool},
- PgConnection,
-};
-use failure::Error;
use log::{debug, info};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
auth: String,
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<ListCategories> {
type Response = ListCategoriesResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<ListCategoriesResponse, Error> {
+ ) -> Result<ListCategoriesResponse, LemmyError> {
let _data: &ListCategories = &self.data;
- let conn = pool.get()?;
-
- let categories: Vec<Category> = Category::list_all(&conn)?;
+ let categories = blocking(pool, move |conn| Category::list_all(conn)).await??;
// Return the jwt
Ok(ListCategoriesResponse { categories })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetModlog> {
type Response = GetModlogResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetModlogResponse, Error> {
+ ) -> Result<GetModlogResponse, LemmyError> {
let data: &GetModlog = &self.data;
- let conn = pool.get()?;
-
- let removed_posts = ModRemovePostView::list(
- &conn,
- data.community_id,
- data.mod_user_id,
- data.page,
- data.limit,
- )?;
- let locked_posts = ModLockPostView::list(
- &conn,
- data.community_id,
- data.mod_user_id,
- data.page,
- data.limit,
- )?;
- let stickied_posts = ModStickyPostView::list(
- &conn,
- data.community_id,
- data.mod_user_id,
- data.page,
- data.limit,
- )?;
- let removed_comments = ModRemoveCommentView::list(
- &conn,
- data.community_id,
- data.mod_user_id,
- data.page,
- data.limit,
- )?;
- let banned_from_community = ModBanFromCommunityView::list(
- &conn,
- data.community_id,
- data.mod_user_id,
- data.page,
- data.limit,
- )?;
- let added_to_community = ModAddCommunityView::list(
- &conn,
- data.community_id,
- data.mod_user_id,
- data.page,
- data.limit,
- )?;
+ let community_id = data.community_id;
+ let mod_user_id = data.mod_user_id;
+ let page = data.page;
+ let limit = data.limit;
+ let removed_posts = blocking(pool, move |conn| {
+ ModRemovePostView::list(conn, community_id, mod_user_id, page, limit)
+ })
+ .await??;
+
+ let locked_posts = blocking(pool, move |conn| {
+ ModLockPostView::list(conn, community_id, mod_user_id, page, limit)
+ })
+ .await??;
+
+ let stickied_posts = blocking(pool, move |conn| {
+ ModStickyPostView::list(conn, community_id, mod_user_id, page, limit)
+ })
+ .await??;
+
+ let removed_comments = blocking(pool, move |conn| {
+ ModRemoveCommentView::list(conn, community_id, mod_user_id, page, limit)
+ })
+ .await??;
+
+ let banned_from_community = blocking(pool, move |conn| {
+ ModBanFromCommunityView::list(conn, community_id, mod_user_id, page, limit)
+ })
+ .await??;
+
+ let added_to_community = blocking(pool, move |conn| {
+ ModAddCommunityView::list(conn, community_id, mod_user_id, page, limit)
+ })
+ .await??;
// These arrays are only for the full modlog, when a community isn't given
let (removed_communities, banned, added) = if data.community_id.is_none() {
- (
- ModRemoveCommunityView::list(&conn, data.mod_user_id, data.page, data.limit)?,
- ModBanView::list(&conn, data.mod_user_id, data.page, data.limit)?,
- ModAddView::list(&conn, data.mod_user_id, data.page, data.limit)?,
- )
+ blocking(pool, move |conn| {
+ Ok((
+ ModRemoveCommunityView::list(conn, mod_user_id, page, limit)?,
+ ModBanView::list(conn, mod_user_id, page, limit)?,
+ ModAddView::list(conn, mod_user_id, page, limit)?,
+ )) as Result<_, LemmyError>
+ })
+ .await??
} else {
(Vec::new(), Vec::new(), Vec::new())
};
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<CreateSite> {
type Response = SiteResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<SiteResponse, Error> {
+ ) -> Result<SiteResponse, LemmyError> {
let data: &CreateSite = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Make sure user is an admin
- if !UserView::read(&conn, user_id)?.admin {
+ let user = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
+ if !user.admin {
return Err(APIError::err("not_an_admin").into());
}
updated: None,
};
- match Site::create(&conn, &site_form) {
- Ok(site) => site,
- Err(_e) => return Err(APIError::err("site_already_exists").into()),
- };
+ let create_site = move |conn: &'_ _| Site::create(conn, &site_form);
+ if blocking(pool, create_site).await?.is_err() {
+ return Err(APIError::err("site_already_exists").into());
+ }
- let site_view = SiteView::read(&conn)?;
+ let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??;
Ok(SiteResponse { site: site_view })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<EditSite> {
type Response = SiteResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<SiteResponse, Error> {
+ ) -> Result<SiteResponse, LemmyError> {
let data: &EditSite = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Make sure user is an admin
- if !UserView::read(&conn, user_id)?.admin {
+ let user = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
+ if !user.admin {
return Err(APIError::err("not_an_admin").into());
}
- let found_site = Site::read(&conn, 1)?;
+ let found_site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
let site_form = SiteForm {
name: data.name.to_owned(),
enable_nsfw: data.enable_nsfw,
};
- match Site::update(&conn, 1, &site_form) {
- Ok(site) => site,
- Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
- };
+ let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
+ if blocking(pool, update_site).await?.is_err() {
+ return Err(APIError::err("couldnt_update_site").into());
+ }
- let site_view = SiteView::read(&conn)?;
+ let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??;
let res = SiteResponse { site: site_view };
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetSite> {
type Response = GetSiteResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetSiteResponse, Error> {
+ ) -> Result<GetSiteResponse, LemmyError> {
let _data: &GetSite = &self.data;
- let conn = pool.get()?;
-
// TODO refactor this a little
- let site_view = if let Ok(_site) = Site::read(&conn, 1) {
- Some(SiteView::read(&conn)?)
+ let res = blocking(pool, move |conn| Site::read(conn, 1)).await?;
+ let site_view = if res.is_ok() {
+ Some(blocking(pool, move |conn| SiteView::read(conn)).await??)
} else if let Some(setup) = Settings::get().setup.as_ref() {
let register = Register {
username: setup.admin_username.to_owned(),
admin: true,
show_nsfw: true,
};
- let login_response = Oper::new(register).perform(pool.clone(), websocket_info.clone())?;
+ let login_response = Oper::new(register, self.client.clone())
+ .perform(pool, websocket_info.clone())
+ .await?;
info!("Admin {} created", setup.admin_username);
let create_site = CreateSite {
enable_nsfw: true,
auth: login_response.jwt,
};
- Oper::new(create_site).perform(pool, websocket_info.clone())?;
+ Oper::new(create_site, self.client.clone())
+ .perform(pool, websocket_info.clone())
+ .await?;
info!("Site {} created", setup.site_name);
- Some(SiteView::read(&conn)?)
+ Some(blocking(pool, move |conn| SiteView::read(conn)).await??)
} else {
None
};
- let mut admins = UserView::admins(&conn)?;
+ let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
// Make sure the site creator is the top admin
if let Some(site_view) = site_view.to_owned() {
}
}
- let banned = UserView::banned(&conn)?;
+ let banned = blocking(pool, move |conn| UserView::banned(conn)).await??;
let online = if let Some(_ws) = websocket_info {
// TODO
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<Search> {
type Response = SearchResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<SearchResponse, Error> {
+ ) -> Result<SearchResponse, LemmyError> {
let data: &Search = &self.data;
dbg!(&data);
- let conn = pool.get()?;
-
- match search_by_apub_id(&data.q, &conn) {
+ match search_by_apub_id(&data.q, &self.client, pool).await {
Ok(r) => return Ok(r),
Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
}
None => None,
};
- let sort = SortType::from_str(&data.sort)?;
let type_ = SearchType::from_str(&data.type_)?;
let mut posts = Vec::new();
// TODO no clean / non-nsfw searching rn
+ let q = data.q.to_owned();
+ let page = data.page;
+ let limit = data.limit;
+ let sort = SortType::from_str(&data.sort)?;
+ let community_id = data.community_id;
match type_ {
SearchType::Posts => {
- posts = PostQueryBuilder::create(&conn)
- .sort(&sort)
- .show_nsfw(true)
- .for_community_id(data.community_id)
- .search_term(data.q.to_owned())
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ posts = blocking(pool, move |conn| {
+ PostQueryBuilder::create(conn)
+ .sort(&sort)
+ .show_nsfw(true)
+ .for_community_id(community_id)
+ .search_term(q)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
}
SearchType::Comments => {
- comments = CommentQueryBuilder::create(&conn)
- .sort(&sort)
- .search_term(data.q.to_owned())
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ comments = blocking(pool, move |conn| {
+ CommentQueryBuilder::create(&conn)
+ .sort(&sort)
+ .search_term(q)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
}
SearchType::Communities => {
- communities = CommunityQueryBuilder::create(&conn)
- .sort(&sort)
- .search_term(data.q.to_owned())
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ communities = blocking(pool, move |conn| {
+ CommunityQueryBuilder::create(conn)
+ .sort(&sort)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
}
SearchType::Users => {
- users = UserQueryBuilder::create(&conn)
- .sort(&sort)
- .search_term(data.q.to_owned())
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ users = blocking(pool, move |conn| {
+ UserQueryBuilder::create(conn)
+ .sort(&sort)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
}
SearchType::All => {
- posts = PostQueryBuilder::create(&conn)
- .sort(&sort)
- .show_nsfw(true)
- .for_community_id(data.community_id)
- .search_term(data.q.to_owned())
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit)
- .list()?;
-
- comments = CommentQueryBuilder::create(&conn)
- .sort(&sort)
- .search_term(data.q.to_owned())
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit)
- .list()?;
-
- communities = CommunityQueryBuilder::create(&conn)
- .sort(&sort)
- .search_term(data.q.to_owned())
- .page(data.page)
- .limit(data.limit)
- .list()?;
-
- users = UserQueryBuilder::create(&conn)
- .sort(&sort)
- .search_term(data.q.to_owned())
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ posts = blocking(pool, move |conn| {
+ PostQueryBuilder::create(conn)
+ .sort(&sort)
+ .show_nsfw(true)
+ .for_community_id(community_id)
+ .search_term(q)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let q = data.q.to_owned();
+ let sort = SortType::from_str(&data.sort)?;
+
+ comments = blocking(pool, move |conn| {
+ CommentQueryBuilder::create(conn)
+ .sort(&sort)
+ .search_term(q)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let q = data.q.to_owned();
+ let sort = SortType::from_str(&data.sort)?;
+
+ communities = blocking(pool, move |conn| {
+ CommunityQueryBuilder::create(conn)
+ .sort(&sort)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let q = data.q.to_owned();
+ let sort = SortType::from_str(&data.sort)?;
+
+ users = blocking(pool, move |conn| {
+ UserQueryBuilder::create(conn)
+ .sort(&sort)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
}
SearchType::Url => {
- posts = PostQueryBuilder::create(&conn)
- .sort(&sort)
- .show_nsfw(true)
- .for_community_id(data.community_id)
- .url_search(data.q.to_owned())
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ posts = blocking(pool, move |conn| {
+ PostQueryBuilder::create(conn)
+ .sort(&sort)
+ .show_nsfw(true)
+ .for_community_id(community_id)
+ .url_search(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
}
};
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<TransferSite> {
type Response = GetSiteResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetSiteResponse, Error> {
+ ) -> Result<GetSiteResponse, LemmyError> {
let data: &TransferSite = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let read_site = Site::read(&conn, 1)?;
+ let read_site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
// Make sure user is the creator
if read_site.creator_id != user_id {
enable_nsfw: read_site.enable_nsfw,
};
- match Site::update(&conn, 1, &site_form) {
- Ok(site) => site,
- Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
+ let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
+ if blocking(pool, update_site).await?.is_err() {
+ return Err(APIError::err("couldnt_update_site").into());
};
// Mod tables
removed: Some(false),
};
- ModAdd::create(&conn, &form)?;
+ blocking(pool, move |conn| ModAdd::create(conn, &form)).await??;
- let site_view = SiteView::read(&conn)?;
+ let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??;
- let mut admins = UserView::admins(&conn)?;
+ let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
let creator_index = admins
.iter()
.position(|r| r.id == site_view.creator_id)
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
- let banned = UserView::banned(&conn)?;
+ let banned = blocking(pool, move |conn| UserView::banned(conn)).await??;
Ok(GetSiteResponse {
site: Some(site_view),
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetSiteConfig> {
type Response = GetSiteConfigResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetSiteConfigResponse, Error> {
+ ) -> Result<GetSiteConfigResponse, LemmyError> {
let data: &GetSiteConfig = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Only let admins read this
- let admins = UserView::admins(&conn)?;
+ let admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
if !admin_ids.contains(&user_id) {
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<SaveSiteConfig> {
type Response = GetSiteConfigResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetSiteConfigResponse, Error> {
+ ) -> Result<GetSiteConfigResponse, LemmyError> {
let data: &SaveSiteConfig = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Only let admins read this
- let admins = UserView::admins(&conn)?;
+ let admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
if !admin_ids.contains(&user_id) {
ApubObjectType,
EndpointType,
},
+ blocking,
db::{
comment::*,
comment_view::*,
UserOperation,
WebsocketInfo,
},
+ DbPool,
+ LemmyError,
};
use bcrypt::verify;
-use diesel::{
- r2d2::{ConnectionManager, Pool},
- PgConnection,
-};
-use failure::Error;
use log::error;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
pub user_id: i32,
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<Login> {
type Response = LoginResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<LoginResponse, Error> {
+ ) -> Result<LoginResponse, LemmyError> {
let data: &Login = &self.data;
- let conn = pool.get()?;
-
// Fetch that username / email
- let user: User_ = match User_::find_by_email_or_username(&conn, &data.username_or_email) {
+ let username_or_email = data.username_or_email.clone();
+ let user = match blocking(pool, move |conn| {
+ User_::find_by_email_or_username(conn, &username_or_email)
+ })
+ .await?
+ {
Ok(user) => user,
Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
};
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<Register> {
type Response = LoginResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<LoginResponse, Error> {
+ ) -> Result<LoginResponse, LemmyError> {
let data: &Register = &self.data;
- let conn = pool.get()?;
-
// Make sure site has open registration
- if let Ok(site) = SiteView::read(&conn) {
+ if let Ok(site) = blocking(pool, move |conn| SiteView::read(conn)).await? {
+ let site: SiteView = site;
if !site.open_registration {
return Err(APIError::err("registration_closed").into());
}
}
// Make sure there are no admins
- if data.admin && !UserView::admins(&conn)?.is_empty() {
+ let any_admins = blocking(pool, move |conn| {
+ UserView::admins(conn).map(|a| a.is_empty())
+ })
+ .await??;
+ if data.admin && !any_admins {
return Err(APIError::err("admin_already_created").into());
}
};
// Create the user
- let inserted_user = match User_::register(&conn, &user_form) {
+ let inserted_user = match blocking(pool, move |conn| User_::register(conn, &user_form)).await? {
Ok(user) => user,
Err(e) => {
let err_type = if e.to_string()
let main_community_keypair = generate_actor_keypair()?;
// Create the main community if it doesn't exist
- let main_community: Community = match Community::read(&conn, 2) {
+ let main_community = match blocking(pool, move |conn| Community::read(conn, 2)).await? {
Ok(c) => c,
Err(_e) => {
let default_community_name = "main";
last_refreshed_at: None,
published: None,
};
- Community::create(&conn, &community_form).unwrap()
+ blocking(pool, move |conn| Community::create(conn, &community_form)).await??
}
};
user_id: inserted_user.id,
};
- let _inserted_community_follower =
- match CommunityFollower::follow(&conn, &community_follower_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
- };
+ let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
+ if blocking(pool, follow).await?.is_err() {
+ return Err(APIError::err("community_follower_already_exists").into());
+ };
// If its an admin, add them as a mod and follower to main
if data.admin {
user_id: inserted_user.id,
};
- let _inserted_community_moderator =
- match CommunityModerator::join(&conn, &community_moderator_form) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
- };
+ let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
+ if blocking(pool, join).await?.is_err() {
+ return Err(APIError::err("community_moderator_already_exists").into());
+ }
}
// Return the jwt
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<SaveUserSettings> {
type Response = LoginResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<LoginResponse, Error> {
+ ) -> Result<LoginResponse, LemmyError> {
let data: &SaveUserSettings = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let read_user = User_::read(&conn, user_id)?;
+ let read_user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
let email = match &data.email {
Some(email) => Some(email.to_owned()),
if !valid {
return Err(APIError::err("password_incorrect").into());
}
- User_::update_password(&conn, user_id, &new_password)?.password_encrypted
+ let new_password = new_password.to_owned();
+ let user = blocking(pool, move |conn| {
+ User_::update_password(conn, user_id, &new_password)
+ })
+ .await??;
+ user.password_encrypted
}
None => return Err(APIError::err("password_incorrect").into()),
}
last_refreshed_at: None,
};
- let updated_user = match User_::update(&conn, user_id, &user_form) {
+ let res = blocking(pool, move |conn| User_::update(conn, user_id, &user_form)).await?;
+ let updated_user: User_ = match res {
Ok(user) => user,
Err(e) => {
let err_type = if e.to_string()
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetUserDetails> {
type Response = GetUserDetailsResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetUserDetailsResponse, Error> {
+ ) -> Result<GetUserDetailsResponse, LemmyError> {
let data: &GetUserDetails = &self.data;
- let conn = pool.get()?;
-
let user_claims: Option<Claims> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Ok(claims) => Some(claims.claims),
let sort = SortType::from_str(&data.sort)?;
+ let username = data
+ .username
+ .to_owned()
+ .unwrap_or_else(|| "admin".to_string());
let user_details_id = match data.user_id {
Some(id) => id,
None => {
- match User_::read_from_name(
- &conn,
- &data
- .username
- .to_owned()
- .unwrap_or_else(|| "admin".to_string()),
- ) {
+ let user = blocking(pool, move |conn| User_::read_from_name(conn, &username)).await?;
+ match user {
Ok(user) => user.id,
Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
}
}
};
- let mut user_view = UserView::read(&conn, user_details_id)?;
-
- let mut posts_query = PostQueryBuilder::create(&conn)
- .sort(&sort)
- .show_nsfw(show_nsfw)
- .saved_only(data.saved_only)
- .for_community_id(data.community_id)
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit);
-
- let mut comments_query = CommentQueryBuilder::create(&conn)
- .sort(&sort)
- .saved_only(data.saved_only)
- .my_user_id(user_id)
- .page(data.page)
- .limit(data.limit);
-
- // If its saved only, you don't care what creator it was
- // Or, if its not saved, then you only want it for that specific creator
- if !data.saved_only {
- posts_query = posts_query.for_creator_id(user_details_id);
- comments_query = comments_query.for_creator_id(user_details_id);
- }
+ let mut user_view = blocking(pool, move |conn| UserView::read(conn, user_details_id)).await??;
+
+ let page = data.page;
+ let limit = data.limit;
+ let saved_only = data.saved_only;
+ let community_id = data.community_id;
+ let (posts, comments) = blocking(pool, move |conn| {
+ let mut posts_query = PostQueryBuilder::create(conn)
+ .sort(&sort)
+ .show_nsfw(show_nsfw)
+ .saved_only(saved_only)
+ .for_community_id(community_id)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit);
+
+ let mut comments_query = CommentQueryBuilder::create(conn)
+ .sort(&sort)
+ .saved_only(saved_only)
+ .my_user_id(user_id)
+ .page(page)
+ .limit(limit);
+
+ // If its saved only, you don't care what creator it was
+ // Or, if its not saved, then you only want it for that specific creator
+ if !saved_only {
+ posts_query = posts_query.for_creator_id(user_details_id);
+ comments_query = comments_query.for_creator_id(user_details_id);
+ }
+
+ let posts = posts_query.list()?;
+ let comments = comments_query.list()?;
+
+ Ok((posts, comments)) as Result<_, LemmyError>
+ })
+ .await??;
+
+ let follows = blocking(pool, move |conn| {
+ CommunityFollowerView::for_user(conn, user_details_id)
+ })
+ .await??;
+ let moderates = blocking(pool, move |conn| {
+ CommunityModeratorView::for_user(conn, user_details_id)
+ })
+ .await??;
- let posts = posts_query.list()?;
- let comments = comments_query.list()?;
+ let site_creator_id =
+ blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
- let follows = CommunityFollowerView::for_user(&conn, user_details_id)?;
- let moderates = CommunityModeratorView::for_user(&conn, user_details_id)?;
- let site_creator_id = Site::read(&conn, 1)?.creator_id;
- let mut admins = UserView::admins(&conn)?;
+ let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<AddAdmin> {
type Response = AddAdminResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<AddAdminResponse, Error> {
+ ) -> Result<AddAdminResponse, LemmyError> {
let data: &AddAdmin = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Make sure user is an admin
- if !UserView::read(&conn, user_id)?.admin {
+ let is_admin = move |conn: &'_ _| UserView::read(conn, user_id).map(|u| u.admin);
+ if !blocking(pool, is_admin).await?? {
return Err(APIError::err("not_an_admin").into());
}
- match User_::add_admin(&conn, user_id, data.added) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
- };
+ let added = data.added;
+ let add_admin = move |conn: &'_ _| User_::add_admin(conn, user_id, added);
+ if blocking(pool, add_admin).await?.is_err() {
+ return Err(APIError::err("couldnt_update_user").into());
+ }
// Mod tables
let form = ModAddForm {
removed: Some(!data.added),
};
- ModAdd::create(&conn, &form)?;
+ blocking(pool, move |conn| ModAdd::create(conn, &form)).await??;
- let site_creator_id = Site::read(&conn, 1)?.creator_id;
- let mut admins = UserView::admins(&conn)?;
+ let site_creator_id =
+ blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
+
+ let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user);
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<BanUser> {
type Response = BanUserResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<BanUserResponse, Error> {
+ ) -> Result<BanUserResponse, LemmyError> {
let data: &BanUser = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
// Make sure user is an admin
- if !UserView::read(&conn, user_id)?.admin {
+ let is_admin = move |conn: &'_ _| UserView::read(conn, user_id).map(|u| u.admin);
+ if !blocking(pool, is_admin).await?? {
return Err(APIError::err("not_an_admin").into());
}
- match User_::ban_user(&conn, user_id, data.ban) {
- Ok(user) => user,
- Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
- };
+ let ban = data.ban;
+ let ban_user = move |conn: &'_ _| User_::ban_user(conn, user_id, ban);
+ if blocking(pool, ban_user).await?.is_err() {
+ return Err(APIError::err("couldnt_update_user").into());
+ }
// Mod tables
let expires = match data.expires {
expires,
};
- ModBan::create(&conn, &form)?;
+ blocking(pool, move |conn| ModBan::create(conn, &form)).await??;
- let user_view = UserView::read(&conn, data.user_id)?;
+ let user_id = data.user_id;
+ let user_view = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
let res = BanUserResponse {
user: user_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetReplies> {
type Response = GetRepliesResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetRepliesResponse, Error> {
+ ) -> Result<GetRepliesResponse, LemmyError> {
let data: &GetReplies = &self.data;
let claims = match Claims::decode(&data.auth) {
let sort = SortType::from_str(&data.sort)?;
- let conn = pool.get()?;
-
- let replies = ReplyQueryBuilder::create(&conn, user_id)
- .sort(&sort)
- .unread_only(data.unread_only)
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ let page = data.page;
+ let limit = data.limit;
+ let unread_only = data.unread_only;
+ let replies = blocking(pool, move |conn| {
+ ReplyQueryBuilder::create(conn, user_id)
+ .sort(&sort)
+ .unread_only(unread_only)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
Ok(GetRepliesResponse { replies })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetUserMentions> {
type Response = GetUserMentionsResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetUserMentionsResponse, Error> {
+ ) -> Result<GetUserMentionsResponse, LemmyError> {
let data: &GetUserMentions = &self.data;
let claims = match Claims::decode(&data.auth) {
let sort = SortType::from_str(&data.sort)?;
- let conn = pool.get()?;
-
- let mentions = UserMentionQueryBuilder::create(&conn, user_id)
- .sort(&sort)
- .unread_only(data.unread_only)
- .page(data.page)
- .limit(data.limit)
- .list()?;
+ let page = data.page;
+ let limit = data.limit;
+ let unread_only = data.unread_only;
+ let mentions = blocking(pool, move |conn| {
+ UserMentionQueryBuilder::create(conn, user_id)
+ .sort(&sort)
+ .unread_only(unread_only)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
Ok(GetUserMentionsResponse { mentions })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<EditUserMention> {
type Response = UserMentionResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<UserMentionResponse, Error> {
+ ) -> Result<UserMentionResponse, LemmyError> {
let data: &EditUserMention = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let user_mention = UserMention::read(&conn, data.user_mention_id)?;
+ let user_mention_id = data.user_mention_id;
+ let user_mention =
+ blocking(pool, move |conn| UserMention::read(conn, user_mention_id)).await??;
let user_mention_form = UserMentionForm {
recipient_id: user_id,
read: data.read.to_owned(),
};
- let _updated_user_mention =
- match UserMention::update(&conn, user_mention.id, &user_mention_form) {
- Ok(comment) => comment,
- Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
- };
+ let user_mention_id = user_mention.id;
+ let update_mention =
+ move |conn: &'_ _| UserMention::update(conn, user_mention_id, &user_mention_form);
+ if blocking(pool, update_mention).await?.is_err() {
+ return Err(APIError::err("couldnt_update_comment").into());
+ };
- let user_mention_view = UserMentionView::read(&conn, user_mention.id, user_id)?;
+ let user_mention_id = user_mention.id;
+ let user_mention_view = blocking(pool, move |conn| {
+ UserMentionView::read(conn, user_mention_id, user_id)
+ })
+ .await??;
Ok(UserMentionResponse {
mention: user_mention_view,
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<MarkAllAsRead> {
type Response = GetRepliesResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<GetRepliesResponse, Error> {
+ ) -> Result<GetRepliesResponse, LemmyError> {
let data: &MarkAllAsRead = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let replies = ReplyQueryBuilder::create(&conn, user_id)
- .unread_only(true)
- .page(1)
- .limit(999)
- .list()?;
+ let replies = blocking(pool, move |conn| {
+ ReplyQueryBuilder::create(conn, user_id)
+ .unread_only(true)
+ .page(1)
+ .limit(999)
+ .list()
+ })
+ .await??;
+ // TODO: this should probably be a bulk operation
for reply in &replies {
- match Comment::mark_as_read(&conn, reply.id) {
- Ok(comment) => comment,
- Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
- };
+ let reply_id = reply.id;
+ let mark_as_read = move |conn: &'_ _| Comment::mark_as_read(conn, reply_id);
+ if blocking(pool, mark_as_read).await?.is_err() {
+ return Err(APIError::err("couldnt_update_comment").into());
+ }
}
// Mentions
- let mentions = UserMentionQueryBuilder::create(&conn, user_id)
- .unread_only(true)
- .page(1)
- .limit(999)
- .list()?;
+ let mentions = blocking(pool, move |conn| {
+ UserMentionQueryBuilder::create(conn, user_id)
+ .unread_only(true)
+ .page(1)
+ .limit(999)
+ .list()
+ })
+ .await??;
+ // TODO: this should probably be a bulk operation
for mention in &mentions {
let mention_form = UserMentionForm {
recipient_id: mention.to_owned().recipient_id,
read: Some(true),
};
- let _updated_mention =
- match UserMention::update(&conn, mention.user_mention_id, &mention_form) {
- Ok(mention) => mention,
- Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
- };
+ let user_mention_id = mention.user_mention_id;
+ let update_mention =
+ move |conn: &'_ _| UserMention::update(conn, user_mention_id, &mention_form);
+ if blocking(pool, update_mention).await?.is_err() {
+ return Err(APIError::err("couldnt_update_comment").into());
+ }
}
// messages
- let messages = PrivateMessageQueryBuilder::create(&conn, user_id)
- .page(1)
- .limit(999)
- .unread_only(true)
- .list()?;
+ let messages = blocking(pool, move |conn| {
+ PrivateMessageQueryBuilder::create(conn, user_id)
+ .page(1)
+ .limit(999)
+ .unread_only(true)
+ .list()
+ })
+ .await??;
+ // TODO: this should probably be a bulk operation
for message in &messages {
let private_message_form = PrivateMessageForm {
content: message.to_owned().content,
published: None,
};
- let _updated_message = match PrivateMessage::update(&conn, message.id, &private_message_form)
- {
- Ok(message) => message,
- Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
- };
+ let message_id = message.id;
+ let update_pm =
+ move |conn: &'_ _| PrivateMessage::update(conn, message_id, &private_message_form);
+ if blocking(pool, update_pm).await?.is_err() {
+ return Err(APIError::err("couldnt_update_private_message").into());
+ }
}
Ok(GetRepliesResponse { replies: vec![] })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<DeleteAccount> {
type Response = LoginResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<LoginResponse, Error> {
+ ) -> Result<LoginResponse, LemmyError> {
let data: &DeleteAccount = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let user: User_ = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
// Verify the password
let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
}
// Comments
- let comments = CommentQueryBuilder::create(&conn)
- .for_creator_id(user_id)
- .limit(std::i64::MAX)
- .list()?;
+ let comments = blocking(pool, move |conn| {
+ CommentQueryBuilder::create(conn)
+ .for_creator_id(user_id)
+ .limit(std::i64::MAX)
+ .list()
+ })
+ .await??;
+ // TODO: this should probably be a bulk operation
for comment in &comments {
- let _updated_comment = match Comment::permadelete(&conn, comment.id) {
- Ok(comment) => comment,
- Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
- };
+ let comment_id = comment.id;
+ let permadelete = move |conn: &'_ _| Comment::permadelete(conn, comment_id);
+ if blocking(pool, permadelete).await?.is_err() {
+ return Err(APIError::err("couldnt_update_comment").into());
+ }
}
// Posts
- let posts = PostQueryBuilder::create(&conn)
- .sort(&SortType::New)
- .for_creator_id(user_id)
- .limit(std::i64::MAX)
- .list()?;
+ let posts = blocking(pool, move |conn| {
+ PostQueryBuilder::create(conn)
+ .sort(&SortType::New)
+ .for_creator_id(user_id)
+ .limit(std::i64::MAX)
+ .list()
+ })
+ .await??;
+ // TODO: this should probably be a bulk operation
for post in &posts {
- let _updated_post = match Post::permadelete(&conn, post.id) {
- Ok(post) => post,
- Err(_e) => return Err(APIError::err("couldnt_update_post").into()),
- };
+ let post_id = post.id;
+ let permadelete = move |conn: &'_ _| Post::permadelete(conn, post_id);
+ if blocking(pool, permadelete).await?.is_err() {
+ return Err(APIError::err("couldnt_update_post").into());
+ }
}
Ok(LoginResponse {
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<PasswordReset> {
type Response = PasswordResetResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<PasswordResetResponse, Error> {
+ ) -> Result<PasswordResetResponse, LemmyError> {
let data: &PasswordReset = &self.data;
- let conn = pool.get()?;
-
// Fetch that email
- let user: User_ = match User_::find_by_email(&conn, &data.email) {
+ let email = data.email.clone();
+ let user = match blocking(pool, move |conn| User_::find_by_email(conn, &email)).await? {
Ok(user) => user,
Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
};
let token = generate_random_string();
// Insert the row
- PasswordResetRequest::create_token(&conn, user.id, &token)?;
+ let token2 = token.clone();
+ let user_id = user.id;
+ blocking(pool, move |conn| {
+ PasswordResetRequest::create_token(conn, user_id, &token2)
+ })
+ .await??;
// Email the pure token to the user.
// TODO no i18n support here.
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<PasswordChange> {
type Response = LoginResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<LoginResponse, Error> {
+ ) -> Result<LoginResponse, LemmyError> {
let data: &PasswordChange = &self.data;
- let conn = pool.get()?;
-
// Fetch the user_id from the token
- let user_id = PasswordResetRequest::read_from_token(&conn, &data.token)?.user_id;
+ let token = data.token.clone();
+ let user_id = blocking(pool, move |conn| {
+ PasswordResetRequest::read_from_token(conn, &token).map(|p| p.user_id)
+ })
+ .await??;
// Make sure passwords match
if data.password != data.password_verify {
}
// Update the user with the new password
- let updated_user = match User_::update_password(&conn, user_id, &data.password) {
+ let password = data.password.clone();
+ let updated_user = match blocking(pool, move |conn| {
+ User_::update_password(conn, user_id, &password)
+ })
+ .await?
+ {
Ok(user) => user,
Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
};
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<CreatePrivateMessage> {
type Response = PrivateMessageResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<PrivateMessageResponse, Error> {
+ ) -> Result<PrivateMessageResponse, LemmyError> {
let data: &CreatePrivateMessage = &self.data;
let claims = match Claims::decode(&data.auth) {
let hostname = &format!("https://{}", Settings::get().hostname);
- let conn = pool.get()?;
-
// Check for a site ban
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
published: None,
};
- let inserted_private_message = match PrivateMessage::create(&conn, &private_message_form) {
+ let inserted_private_message = match blocking(pool, move |conn| {
+ PrivateMessage::create(conn, &private_message_form)
+ })
+ .await?
+ {
Ok(private_message) => private_message,
Err(_e) => {
return Err(APIError::err("couldnt_create_private_message").into());
}
};
- let updated_private_message =
- match PrivateMessage::update_ap_id(&conn, inserted_private_message.id) {
- Ok(private_message) => private_message,
- Err(_e) => return Err(APIError::err("couldnt_create_private_message").into()),
- };
+ let inserted_private_message_id = inserted_private_message.id;
+ let updated_private_message = match blocking(pool, move |conn| {
+ PrivateMessage::update_ap_id(&conn, inserted_private_message_id)
+ })
+ .await?
+ {
+ Ok(private_message) => private_message,
+ Err(_e) => return Err(APIError::err("couldnt_create_private_message").into()),
+ };
- updated_private_message.send_create(&user, &conn)?;
+ updated_private_message
+ .send_create(&user, &self.client, pool)
+ .await?;
// Send notifications to the recipient
- let recipient_user = User_::read(&conn, data.recipient_id)?;
+ let recipient_id = data.recipient_id;
+ let recipient_user = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
if recipient_user.send_notifications_to_email {
if let Some(email) = recipient_user.email {
let subject = &format!(
}
}
- let message = PrivateMessageView::read(&conn, inserted_private_message.id)?;
+ let message = blocking(pool, move |conn| {
+ PrivateMessageView::read(conn, inserted_private_message.id)
+ })
+ .await??;
let res = PrivateMessageResponse { message };
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<EditPrivateMessage> {
type Response = PrivateMessageResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<PrivateMessageResponse, Error> {
+ ) -> Result<PrivateMessageResponse, LemmyError> {
let data: &EditPrivateMessage = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let orig_private_message = PrivateMessage::read(&conn, data.edit_id)?;
+ let edit_id = data.edit_id;
+ let orig_private_message =
+ blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
// Check for a site ban
- let user = User_::read(&conn, user_id)?;
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
published: None,
};
- let updated_private_message =
- match PrivateMessage::update(&conn, data.edit_id, &private_message_form) {
- Ok(private_message) => private_message,
- Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
- };
+ let edit_id = data.edit_id;
+ let updated_private_message = match blocking(pool, move |conn| {
+ PrivateMessage::update(conn, edit_id, &private_message_form)
+ })
+ .await?
+ {
+ Ok(private_message) => private_message,
+ Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
+ };
if let Some(deleted) = data.deleted.to_owned() {
if deleted {
- updated_private_message.send_delete(&user, &conn)?;
+ updated_private_message
+ .send_delete(&user, &self.client, pool)
+ .await?;
} else {
- updated_private_message.send_undo_delete(&user, &conn)?;
+ updated_private_message
+ .send_undo_delete(&user, &self.client, pool)
+ .await?;
}
} else {
- updated_private_message.send_update(&user, &conn)?;
+ updated_private_message
+ .send_update(&user, &self.client, pool)
+ .await?;
}
- let message = PrivateMessageView::read(&conn, data.edit_id)?;
+ let edit_id = data.edit_id;
+ let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
let res = PrivateMessageResponse { message };
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<GetPrivateMessages> {
type Response = PrivateMessagesResponse;
- fn perform(
+ async fn perform(
&self,
- pool: Pool<ConnectionManager<PgConnection>>,
+ pool: &DbPool,
_websocket_info: Option<WebsocketInfo>,
- ) -> Result<PrivateMessagesResponse, Error> {
+ ) -> Result<PrivateMessagesResponse, LemmyError> {
let data: &GetPrivateMessages = &self.data;
let claims = match Claims::decode(&data.auth) {
let user_id = claims.id;
- let conn = pool.get()?;
-
- let messages = PrivateMessageQueryBuilder::create(&conn, user_id)
- .page(data.page)
- .limit(data.limit)
- .unread_only(data.unread_only)
- .list()?;
+ let page = data.page;
+ let limit = data.limit;
+ let unread_only = data.unread_only;
+ let messages = blocking(pool, move |conn| {
+ PrivateMessageQueryBuilder::create(&conn, user_id)
+ .page(page)
+ .limit(limit)
+ .unread_only(unread_only)
+ .list()
+ })
+ .await??;
Ok(PrivateMessagesResponse { messages })
}
}
+#[async_trait::async_trait(?Send)]
impl Perform for Oper<UserJoin> {
type Response = UserJoinResponse;
- fn perform(
+ async fn perform(
&self,
- _pool: Pool<ConnectionManager<PgConnection>>,
+ _pool: &DbPool,
websocket_info: Option<WebsocketInfo>,
- ) -> Result<UserJoinResponse, Error> {
+ ) -> Result<UserJoinResponse, LemmyError> {
let data: &UserJoin = &self.data;
let claims = match Claims::decode(&data.auth) {
use crate::{
apub::{extensions::signatures::sign, is_apub_id_valid, ActorType},
db::{activity::insert_activity, community::Community, user::User_},
+ request::retry_custom,
+ DbPool,
+ LemmyError,
};
use activitystreams::{context, object::properties::ObjectProperties, public, Activity, Base};
-use diesel::PgConnection;
-use failure::{Error, _core::fmt::Debug};
-use isahc::prelude::*;
+use actix_web::client::Client;
use log::debug;
use serde::Serialize;
+use std::fmt::Debug;
use url::Url;
pub fn populate_object_props(
props: &mut ObjectProperties,
addressed_ccs: Vec<String>,
object_id: &str,
-) -> Result<(), Error> {
+) -> Result<(), LemmyError> {
props
.set_context_xsd_any_uri(context())?
// TODO: the activity needs a seperate id from the object
Ok(())
}
-pub fn send_activity_to_community<A>(
+pub async fn send_activity_to_community<A>(
creator: &User_,
- conn: &PgConnection,
community: &Community,
to: Vec<String>,
activity: A,
-) -> Result<(), Error>
+ client: &Client,
+ pool: &DbPool,
+) -> Result<(), LemmyError>
where
- A: Activity + Base + Serialize + Debug,
+ A: Activity + Base + Serialize + Debug + Clone + Send + 'static,
{
- insert_activity(&conn, creator.id, &activity, true)?;
+ insert_activity(creator.id, activity.clone(), true, pool).await?;
// if this is a local community, we need to do an announce from the community instead
if community.local {
- Community::do_announce(activity, &community, creator, conn)?;
+ Community::do_announce(activity, &community, creator, client, pool).await?;
} else {
- send_activity(&activity, creator, to)?;
+ send_activity(client, &activity, creator, to).await?;
}
+
Ok(())
}
/// Send an activity to a list of recipients, using the correct headers etc.
-pub fn send_activity<A>(activity: &A, actor: &dyn ActorType, to: Vec<String>) -> Result<(), Error>
+pub async fn send_activity<A>(
+ client: &Client,
+ activity: &A,
+ actor: &dyn ActorType,
+ to: Vec<String>,
+) -> Result<(), LemmyError>
where
- A: Serialize + Debug,
+ A: Serialize,
{
- let json = serde_json::to_string(&activity)?;
- debug!("Sending activitypub activity {} to {:?}", json, to);
+ let activity = serde_json::to_string(&activity)?;
+ debug!("Sending activitypub activity {} to {:?}", activity, to);
+
for t in to {
let to_url = Url::parse(&t)?;
if !is_apub_id_valid(&to_url) {
debug!("Not sending activity to {} (invalid or blocklisted)", t);
continue;
}
- let request = Request::post(t).header("Host", to_url.domain().unwrap());
- let signature = sign(&request, actor)?;
- let res = request
- .header("Signature", signature)
- .header("Content-Type", "application/json")
- .body(json.to_owned())?
- .send()?;
+
+ let res = retry_custom(|| async {
+ let request = client.post(&t).header("Content-Type", "application/json");
+
+ match sign(request, actor, activity.clone()).await {
+ Ok(signed) => Ok(signed.send().await),
+ Err(e) => Err(e),
+ }
+ })
+ .await?;
+
debug!("Result for activity send: {:?}", res);
}
+
Ok(())
}
FromApub,
ToApub,
},
+ blocking,
convert_datetime,
db::{
comment::{Comment, CommentForm},
},
routes::DbPoolParam,
scrape_text_for_mentions,
+ DbPool,
+ LemmyError,
MentionData,
};
use activitystreams::{
object::{kind::NoteType, properties::ObjectProperties, Note},
};
use activitystreams_new::object::Tombstone;
-use actix_web::{body::Body, web::Path, HttpResponse, Result};
-use diesel::PgConnection;
-use failure::Error;
+use actix_web::{body::Body, client::Client, web::Path, HttpResponse};
use itertools::Itertools;
use log::debug;
use serde::Deserialize;
pub async fn get_apub_comment(
info: Path<CommentQuery>,
db: DbPoolParam,
-) -> Result<HttpResponse<Body>, Error> {
+) -> Result<HttpResponse<Body>, LemmyError> {
let id = info.comment_id.parse::<i32>()?;
- let comment = Comment::read(&&db.get()?, id)?;
+ let comment = blocking(&db, move |conn| Comment::read(conn, id)).await??;
+
if !comment.deleted {
- Ok(create_apub_response(&comment.to_apub(&db.get().unwrap())?))
+ Ok(create_apub_response(&comment.to_apub(&db).await?))
} else {
Ok(create_apub_tombstone_response(&comment.to_tombstone()?))
}
}
+#[async_trait::async_trait(?Send)]
impl ToApub for Comment {
type Response = Note;
- fn to_apub(&self, conn: &PgConnection) -> Result<Note, Error> {
+ async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let mut comment = Note::default();
let oprops: &mut ObjectProperties = comment.as_mut();
- let creator = User_::read(&conn, self.creator_id)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+
+ let creator_id = self.creator_id;
+ let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
// Add a vector containing some important info to the "in_reply_to" field
// [post_ap_id, Option(parent_comment_ap_id)]
let mut in_reply_to_vec = vec![post.ap_id];
if let Some(parent_id) = self.parent_id {
- let parent_comment = Comment::read(&conn, parent_id)?;
+ let parent_comment = blocking(pool, move |conn| Comment::read(conn, parent_id)).await??;
+
in_reply_to_vec.push(parent_comment.ap_id);
}
Ok(comment)
}
- fn to_tombstone(&self) -> Result<Tombstone, Error> {
+ fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
create_tombstone(
self.deleted,
&self.ap_id,
}
}
+#[async_trait::async_trait(?Send)]
impl FromApub for CommentForm {
type ApubType = Note;
/// Parse an ActivityPub note received from another instance into a Lemmy comment
- fn from_apub(note: &Note, conn: &PgConnection) -> Result<CommentForm, Error> {
+ async fn from_apub(
+ note: &Note,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<CommentForm, LemmyError> {
let oprops = ¬e.object_props;
let creator_actor_id = &oprops.get_attributed_to_xsd_any_uri().unwrap().to_string();
- let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, &conn)?;
+
+ let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, client, pool).await?;
let mut in_reply_tos = oprops.get_many_in_reply_to_xsd_any_uris().unwrap();
let post_ap_id = in_reply_tos.next().unwrap().to_string();
// This post, or the parent comment might not yet exist on this server yet, fetch them.
- let post = get_or_fetch_and_insert_remote_post(&post_ap_id, &conn)?;
+ let post = get_or_fetch_and_insert_remote_post(&post_ap_id, client, pool).await?;
// The 2nd item, if it exists, is the parent comment apub_id
// For deeply nested comments, FromApub automatically gets called recursively
let parent_id: Option<i32> = match in_reply_tos.next() {
Some(parent_comment_uri) => {
let parent_comment_ap_id = &parent_comment_uri.to_string();
- let parent_comment = get_or_fetch_and_insert_remote_comment(&parent_comment_ap_id, &conn)?;
+ let parent_comment =
+ get_or_fetch_and_insert_remote_comment(&parent_comment_ap_id, client, pool).await?;
Some(parent_comment.id)
}
}
}
+#[async_trait::async_trait(?Send)]
impl ApubObjectType for Comment {
/// Send out information about a newly created comment, to the followers of the community.
- fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(conn, post.community_id)?;
- let id = format!("{}/create/{}", self.ap_id, uuid::Uuid::new_v4());
+ async fn send_create(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
- let maa: MentionsAndAddresses =
- collect_non_local_mentions_and_addresses(&conn, &self.content, &community)?;
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
+ let maa =
+ collect_non_local_mentions_and_addresses(&self.content, &community, client, pool).await?;
+
+ let id = format!("{}/create/{}", self.ap_id, uuid::Uuid::new_v4());
let mut create = Create::new();
populate_object_props(&mut create.object_props, maa.addressed_ccs, &id)?;
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(note)?;
- send_activity_to_community(&creator, &conn, &community, maa.inboxes, create)?;
+ send_activity_to_community(&creator, &community, maa.inboxes, create, client, pool).await?;
Ok(())
}
/// Send out information about an edited post, to the followers of the community.
- fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
- let id = format!("{}/update/{}", self.ap_id, uuid::Uuid::new_v4());
+ async fn send_update(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
- let maa: MentionsAndAddresses =
- collect_non_local_mentions_and_addresses(&conn, &self.content, &community)?;
+ let maa =
+ collect_non_local_mentions_and_addresses(&self.content, &community, client, pool).await?;
+ let id = format!("{}/update/{}", self.ap_id, uuid::Uuid::new_v4());
let mut update = Update::new();
populate_object_props(&mut update.object_props, maa.addressed_ccs, &id)?;
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(note)?;
- send_activity_to_community(&creator, &conn, &community, maa.inboxes, update)?;
+ send_activity_to_community(&creator, &community, maa.inboxes, update, client, pool).await?;
Ok(())
}
- fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+ async fn send_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
let mut delete = Delete::default();
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
delete,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+ async fn send_undo_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
// Generate a fake delete activity, with the correct object
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
undo,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+ async fn send_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
let mut remove = Remove::default();
send_activity_to_community(
&mod_,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
remove,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+ async fn send_undo_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
// Generate a fake delete activity, with the correct object
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
send_activity_to_community(
&mod_,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
undo,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
}
+#[async_trait::async_trait(?Send)]
impl ApubLikeableType for Comment {
- fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+ async fn send_like(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4());
let mut like = Like::new();
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
like,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_dislike(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+ async fn send_dislike(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/dislike/{}", self.ap_id, uuid::Uuid::new_v4());
let mut dislike = Dislike::new();
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
dislike,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_undo_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(&conn)?;
- let post = Post::read(&conn, self.post_id)?;
- let community = Community::read(&conn, post.community_id)?;
+ async fn send_undo_like(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
+
+ let post_id = self.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+
+ let community_id = post.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/dislike/{}", self.ap_id, uuid::Uuid::new_v4());
let mut like = Like::new();
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
undo,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
}
/// This takes a comment, and builds a list of to_addresses, inboxes,
/// and mention tags, so they know where to be sent to.
/// Addresses are the users / addresses that go in the cc field.
-fn collect_non_local_mentions_and_addresses(
- conn: &PgConnection,
+async fn collect_non_local_mentions_and_addresses(
content: &str,
community: &Community,
-) -> Result<MentionsAndAddresses, Error> {
+ client: &Client,
+ pool: &DbPool,
+) -> Result<MentionsAndAddresses, LemmyError> {
let mut addressed_ccs = vec![community.get_followers_url()];
// Add the mention tag
// Filter only the non-local ones
.filter(|m| !m.is_local())
.collect::<Vec<MentionData>>();
+
let mut mention_inboxes = Vec::new();
for mention in &mentions {
// TODO should it be fetching it every time?
- if let Ok(actor_id) = fetch_webfinger_url(mention) {
+ if let Ok(actor_id) = fetch_webfinger_url(mention, client).await {
debug!("mention actor_id: {}", actor_id);
addressed_ccs.push(actor_id.to_owned());
- let mention_user = get_or_fetch_and_upsert_remote_user(&actor_id, &conn)?;
+
+ let mention_user = get_or_fetch_and_upsert_remote_user(&actor_id, client, pool).await?;
let shared_inbox = mention_user.get_shared_inbox_url();
+
mention_inboxes.push(shared_inbox);
let mut mention_tag = Mention::new();
mention_tag
GroupExt,
ToApub,
},
+ blocking,
convert_datetime,
db::{
activity::insert_activity,
},
naive_now,
routes::DbPoolParam,
+ DbPool,
+ LemmyError,
};
use activitystreams::{
activity::{Accept, Announce, Delete, Remove, Undo},
};
use activitystreams_ext::Ext3;
use activitystreams_new::{activity::Follow, object::Tombstone};
-use actix_web::{body::Body, web::Path, HttpResponse, Result};
-use diesel::PgConnection;
-use failure::{Error, _core::fmt::Debug};
+use actix_web::{body::Body, client::Client, web, HttpResponse};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
+use std::fmt::Debug;
#[derive(Deserialize)]
pub struct CommunityQuery {
community_name: String,
}
+#[async_trait::async_trait(?Send)]
impl ToApub for Community {
type Response = GroupExt;
// Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
- fn to_apub(&self, conn: &PgConnection) -> Result<GroupExt, Error> {
+ async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
let mut group = Group::default();
let oprops: &mut ObjectProperties = group.as_mut();
// then the rest of the moderators
// TODO Technically the instance admins can mod the community, but lets
// ignore that for now
- let moderators = CommunityModeratorView::for_community(&conn, self.id)?
- .into_iter()
- .map(|m| m.user_actor_id)
- .collect();
+ let id = self.id;
+ let moderators = blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(&conn, id)
+ })
+ .await??;
+ let moderators = moderators.into_iter().map(|m| m.user_actor_id).collect();
oprops
.set_context_xsd_any_uri(context())?
.set_endpoints(endpoint_props)?
.set_followers(self.get_followers_url())?;
- let group_extension = GroupExtension::new(conn, self.category_id, self.nsfw)?;
+ let nsfw = self.nsfw;
+ let category_id = self.category_id;
+ let group_extension = blocking(pool, move |conn| {
+ GroupExtension::new(conn, category_id, nsfw)
+ })
+ .await??;
Ok(Ext3::new(
group,
))
}
- fn to_tombstone(&self) -> Result<Tombstone, Error> {
+ fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
create_tombstone(
self.deleted,
&self.actor_id,
}
}
+#[async_trait::async_trait(?Send)]
impl ActorType for Community {
fn actor_id(&self) -> String {
self.actor_id.to_owned()
}
/// As a local community, accept the follow request from a remote user.
- fn send_accept_follow(&self, follow: &Follow, conn: &PgConnection) -> Result<(), Error> {
+ async fn send_accept_follow(
+ &self,
+ follow: &Follow,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
let actor_uri = follow.actor.as_single_xsd_any_uri().unwrap().to_string();
let id = format!("{}/accept/{}", self.actor_id, uuid::Uuid::new_v4());
.set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
let to = format!("{}/inbox", actor_uri);
- insert_activity(&conn, self.creator_id, &accept, true)?;
+ insert_activity(self.creator_id, accept.clone(), true, pool).await?;
- send_activity(&accept, self, vec![to])?;
+ send_activity(client, &accept, self, vec![to]).await?;
Ok(())
}
- fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let group = self.to_apub(conn)?;
+ async fn send_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let group = self.to_apub(pool).await?;
+
let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4());
let mut delete = Delete::default();
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(BaseBox::from_concrete(group)?)?;
- insert_activity(&conn, self.creator_id, &delete, true)?;
+ insert_activity(self.creator_id, delete.clone(), true, pool).await?;
+
+ let inboxes = self.get_follower_inboxes(pool).await?;
// Note: For an accept, since it was automatic, no one pushed a button,
// the community was the actor.
// But for delete, the creator is the actor, and does the signing
- send_activity(&delete, creator, self.get_follower_inboxes(&conn)?)?;
+ send_activity(client, &delete, creator, inboxes).await?;
Ok(())
}
- fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let group = self.to_apub(conn)?;
+ async fn send_undo_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let group = self.to_apub(pool).await?;
+
let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4());
let mut delete = Delete::default();
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(delete)?;
- insert_activity(&conn, self.creator_id, &undo, true)?;
+ insert_activity(self.creator_id, undo.clone(), true, pool).await?;
+
+ let inboxes = self.get_follower_inboxes(pool).await?;
// Note: For an accept, since it was automatic, no one pushed a button,
// the community was the actor.
// But for delete, the creator is the actor, and does the signing
- send_activity(&undo, creator, self.get_follower_inboxes(&conn)?)?;
+ send_activity(client, &undo, creator, inboxes).await?;
Ok(())
}
- fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
- let group = self.to_apub(conn)?;
+ async fn send_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let group = self.to_apub(pool).await?;
+
let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4());
let mut remove = Remove::default();
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
.set_object_base_box(BaseBox::from_concrete(group)?)?;
- insert_activity(&conn, mod_.id, &remove, true)?;
+ insert_activity(mod_.id, remove.clone(), true, pool).await?;
+
+ let inboxes = self.get_follower_inboxes(pool).await?;
// Note: For an accept, since it was automatic, no one pushed a button,
// the community was the actor.
// But for delete, the creator is the actor, and does the signing
- send_activity(&remove, mod_, self.get_follower_inboxes(&conn)?)?;
+ send_activity(client, &remove, mod_, inboxes).await?;
Ok(())
}
- fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
- let group = self.to_apub(conn)?;
+ async fn send_undo_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let group = self.to_apub(pool).await?;
+
let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4());
let mut remove = Remove::default();
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
.set_object_base_box(remove)?;
- insert_activity(&conn, mod_.id, &undo, true)?;
+ insert_activity(mod_.id, undo.clone(), true, pool).await?;
+
+ let inboxes = self.get_follower_inboxes(pool).await?;
// Note: For an accept, since it was automatic, no one pushed a button,
// the community was the actor.
// But for remove , the creator is the actor, and does the signing
- send_activity(&undo, mod_, self.get_follower_inboxes(&conn)?)?;
+ send_activity(client, &undo, mod_, inboxes).await?;
Ok(())
}
/// For a given community, returns the inboxes of all followers.
- fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
- Ok(
- CommunityFollowerView::for_community(conn, self.id)?
- .into_iter()
- .map(|c| get_shared_inbox(&c.user_actor_id))
- .filter(|s| !s.is_empty())
- .unique()
- .collect(),
- )
+ async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<String>, LemmyError> {
+ let id = self.id;
+
+ let inboxes = blocking(pool, move |conn| {
+ CommunityFollowerView::for_community(conn, id)
+ })
+ .await??;
+ let inboxes = inboxes
+ .into_iter()
+ .map(|c| get_shared_inbox(&c.user_actor_id))
+ .filter(|s| !s.is_empty())
+ .unique()
+ .collect();
+
+ Ok(inboxes)
}
- fn send_follow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_follow(
+ &self,
+ _follow_actor_id: &str,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
- fn send_unfollow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_unfollow(
+ &self,
+ _follow_actor_id: &str,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
}
+#[async_trait::async_trait(?Send)]
impl FromApub for CommunityForm {
type ApubType = GroupExt;
/// Parse an ActivityPub group received from another instance into a Lemmy community.
- fn from_apub(group: &GroupExt, conn: &PgConnection) -> Result<Self, Error> {
+ async fn from_apub(group: &GroupExt, client: &Client, pool: &DbPool) -> Result<Self, LemmyError> {
let group_extensions: &GroupExtension = &group.ext_one;
let oprops = &group.inner.object_props;
let aprops = &group.ext_two;
let public_key: &PublicKey = &group.ext_three.public_key;
let mut creator_and_moderator_uris = oprops.get_many_attributed_to_xsd_any_uris().unwrap();
- let creator = creator_and_moderator_uris
- .next()
- .map(|c| get_or_fetch_and_upsert_remote_user(&c.to_string(), &conn).unwrap())
- .unwrap();
+ let creator_uri = creator_and_moderator_uris.next().unwrap();
+
+ let creator = get_or_fetch_and_upsert_remote_user(creator_uri.as_str(), client, pool).await?;
Ok(CommunityForm {
name: oprops.get_name_xsd_string().unwrap().to_string(),
/// Return the community json over HTTP.
pub async fn get_apub_community_http(
- info: Path<CommunityQuery>,
+ info: web::Path<CommunityQuery>,
db: DbPoolParam,
-) -> Result<HttpResponse<Body>, Error> {
- let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
+) -> Result<HttpResponse<Body>, LemmyError> {
+ let community = blocking(&db, move |conn| {
+ Community::read_from_name(conn, &info.community_name)
+ })
+ .await??;
+
if !community.deleted {
- Ok(create_apub_response(
- &community.to_apub(&db.get().unwrap())?,
- ))
+ let apub = community.to_apub(&db).await?;
+
+ Ok(create_apub_response(&apub))
} else {
Ok(create_apub_tombstone_response(&community.to_tombstone()?))
}
/// Returns an empty followers collection, only populating the size (for privacy).
pub async fn get_apub_community_followers(
- info: Path<CommunityQuery>,
+ info: web::Path<CommunityQuery>,
db: DbPoolParam,
-) -> Result<HttpResponse<Body>, Error> {
- let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
-
- let conn = db.get()?;
-
- //As we are an object, we validated that the community id was valid
- let community_followers = CommunityFollowerView::for_community(&conn, community.id).unwrap();
+) -> Result<HttpResponse<Body>, LemmyError> {
+ let community = blocking(&db, move |conn| {
+ Community::read_from_name(&conn, &info.community_name)
+ })
+ .await??;
+
+ let community_id = community.id;
+ let community_followers = blocking(&db, move |conn| {
+ CommunityFollowerView::for_community(&conn, community_id)
+ })
+ .await??;
let mut collection = UnorderedCollection::default();
let oprops: &mut ObjectProperties = collection.as_mut();
}
impl Community {
- pub fn do_announce<A>(
+ pub async fn do_announce<A>(
activity: A,
community: &Community,
sender: &dyn ActorType,
- conn: &PgConnection,
- ) -> Result<HttpResponse, Error>
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<HttpResponse, LemmyError>
where
A: Activity + Base + Serialize + Debug,
{
.set_actor_xsd_any_uri(community.actor_id.to_owned())?
.set_object_base_box(BaseBox::from_concrete(activity)?)?;
- insert_activity(&conn, community.creator_id, &announce, true)?;
+ insert_activity(community.creator_id, announce.clone(), true, pool).await?;
// dont send to the instance where the activity originally came from, because that would result
// in a database error (same data inserted twice)
- let mut to = community.get_follower_inboxes(&conn)?;
+ let mut to = community.get_follower_inboxes(pool).await?;
+
// this seems to be the "easiest" stable alternative for remove_item()
to.retain(|x| *x != sender.get_shared_inbox_url());
- send_activity(&announce, community, to)?;
+ send_activity(client, &announce, community, to).await?;
Ok(HttpResponse::Ok().finish())
}
fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user},
ActorType,
},
+ blocking,
db::{
activity::insert_activity,
community::{Community, CommunityFollower, CommunityFollowerForm},
Followable,
},
routes::{ChatServerParam, DbPoolParam},
+ LemmyError,
};
use activitystreams::activity::Undo;
use activitystreams_new::activity::Follow;
-use actix_web::{web, HttpRequest, HttpResponse, Result};
-use diesel::PgConnection;
-use failure::{Error, _core::fmt::Debug};
+use actix_web::{client::Client, web, HttpRequest, HttpResponse};
use log::debug;
use serde::Deserialize;
+use std::fmt::Debug;
#[serde(untagged)]
#[derive(Deserialize, Debug)]
}
impl CommunityAcceptedObjects {
- fn follow(&self) -> Result<Follow, Error> {
+ fn follow(&self) -> Result<Follow, LemmyError> {
match self {
CommunityAcceptedObjects::Follow(f) => Ok(f.to_owned()),
CommunityAcceptedObjects::Undo(u) => Ok(
input: web::Json<CommunityAcceptedObjects>,
path: web::Path<String>,
db: DbPoolParam,
+ client: web::Data<Client>,
_chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let input = input.into_inner();
- let conn = db.get()?;
- let community = Community::read_from_name(&conn, &path.into_inner())?;
+
+ let path = path.into_inner();
+ let community = blocking(&db, move |conn| Community::read_from_name(&conn, &path)).await??;
+
if !community.local {
- return Err(format_err!(
- "Received activity is addressed to remote community {}",
- &community.actor_id
- ));
+ return Err(
+ format_err!(
+ "Received activity is addressed to remote community {}",
+ &community.actor_id
+ )
+ .into(),
+ );
}
debug!(
"Community {} received activity {:?}",
let user_uri = follow.actor.as_single_xsd_any_uri().unwrap().to_string();
let community_uri = follow.object.as_single_xsd_any_uri().unwrap().to_string();
- let conn = db.get()?;
-
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
- let community = get_or_fetch_and_upsert_remote_community(&community_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, &client, &db).await?;
+ let community = get_or_fetch_and_upsert_remote_community(&community_uri, &client, &db).await?;
verify(&request, &user)?;
match input {
- CommunityAcceptedObjects::Follow(f) => handle_follow(&f, &user, &community, &conn),
- CommunityAcceptedObjects::Undo(u) => handle_undo_follow(&u, &user, &community, &conn),
+ CommunityAcceptedObjects::Follow(f) => handle_follow(f, user, community, &client, db).await,
+ CommunityAcceptedObjects::Undo(u) => handle_undo_follow(u, user, community, db).await,
}
}
/// Handle a follow request from a remote user, adding it to the local database and returning an
/// Accept activity.
-fn handle_follow(
- follow: &Follow,
- user: &User_,
- community: &Community,
- conn: &PgConnection,
-) -> Result<HttpResponse, Error> {
- insert_activity(&conn, user.id, &follow, false)?;
+async fn handle_follow(
+ follow: Follow,
+ user: User_,
+ community: Community,
+ client: &Client,
+ db: DbPoolParam,
+) -> Result<HttpResponse, LemmyError> {
+ insert_activity(user.id, follow.clone(), false, &db).await?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
};
// This will fail if they're already a follower, but ignore the error.
- CommunityFollower::follow(&conn, &community_follower_form).ok();
+ blocking(&db, move |conn| {
+ CommunityFollower::follow(&conn, &community_follower_form).ok()
+ })
+ .await?;
- community.send_accept_follow(&follow, &conn)?;
+ community.send_accept_follow(&follow, &client, &db).await?;
Ok(HttpResponse::Ok().finish())
}
-fn handle_undo_follow(
- undo: &Undo,
- user: &User_,
- community: &Community,
- conn: &PgConnection,
-) -> Result<HttpResponse, Error> {
- insert_activity(&conn, user.id, &undo, false)?;
+async fn handle_undo_follow(
+ undo: Undo,
+ user: User_,
+ community: Community,
+ db: DbPoolParam,
+) -> Result<HttpResponse, LemmyError> {
+ insert_activity(user.id, undo, false, &db).await?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
user_id: user.id,
};
- CommunityFollower::unfollow(&conn, &community_follower_form).ok();
+ // This will fail if they aren't a follower, but ignore the error.
+ blocking(&db, move |conn| {
+ CommunityFollower::unfollow(&conn, &community_follower_form).ok()
+ })
+ .await?;
Ok(HttpResponse::Ok().finish())
}
-use crate::db::{category::Category, Crud};
+use crate::{
+ db::{category::Category, Crud},
+ LemmyError,
+};
use activitystreams::{ext::Extension, Actor};
use diesel::PgConnection;
-use failure::Error;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
conn: &PgConnection,
category_id: i32,
sensitive: bool,
- ) -> Result<GroupExtension, Error> {
+ ) -> Result<GroupExtension, LemmyError> {
let category = Category::read(conn, category_id)?;
let group_category = GroupCategory {
identifier: category_id.to_string(),
-use crate::apub::ActorType;
+use crate::{apub::ActorType, LemmyError};
use activitystreams::ext::Extension;
-use actix_web::HttpRequest;
-use failure::Error;
-use http::request::Builder;
-use http_signature_normalization::Config;
+use actix_web::{client::ClientRequest, HttpRequest};
+use http_signature_normalization_actix::{
+ digest::{DigestClient, SignExt},
+ Config,
+};
use log::debug;
use openssl::{
hash::MessageDigest,
sign::{Signer, Verifier},
};
use serde::{Deserialize, Serialize};
-use std::collections::BTreeMap;
+use sha2::{Digest, Sha256};
lazy_static! {
static ref HTTP_SIG_CONFIG: Config = Config::new();
}
/// Generate the asymmetric keypair for ActivityPub HTTP signatures.
-pub fn generate_actor_keypair() -> Result<Keypair, Error> {
+pub fn generate_actor_keypair() -> Result<Keypair, LemmyError> {
let rsa = Rsa::generate(2048)?;
let pkey = PKey::from_rsa(rsa)?;
let public_key = pkey.public_key_to_pem()?;
}
/// Signs request headers with the given keypair.
-pub fn sign(request: &Builder, actor: &dyn ActorType) -> Result<String, Error> {
+pub async fn sign(
+ request: ClientRequest,
+ actor: &dyn ActorType,
+ activity: String,
+) -> Result<DigestClient<String>, LemmyError> {
let signing_key_id = format!("{}#main-key", actor.actor_id());
+ let private_key = actor.private_key();
- let headers = request
- .headers_ref()
- .unwrap()
- .iter()
- .map(|h| -> Result<(String, String), Error> {
- Ok((h.0.as_str().to_owned(), h.1.to_str()?.to_owned()))
- })
- .collect::<Result<BTreeMap<String, String>, Error>>()?;
+ let digest_client = request
+ .signature_with_digest(
+ HTTP_SIG_CONFIG.clone(),
+ signing_key_id,
+ Sha256::new(),
+ activity,
+ move |signing_string| {
+ let private_key = PKey::private_key_from_pem(private_key.as_bytes())?;
+ let mut signer = Signer::new(MessageDigest::sha256(), &private_key).unwrap();
+ signer.update(signing_string.as_bytes()).unwrap();
- let signature_header_value = HTTP_SIG_CONFIG
- .begin_sign(
- request.method_ref().unwrap().as_str(),
- request
- .uri_ref()
- .unwrap()
- .path_and_query()
- .unwrap()
- .as_str(),
- headers,
- )?
- .sign(signing_key_id, |signing_string| {
- let private_key = PKey::private_key_from_pem(actor.private_key().as_bytes())?;
- let mut signer = Signer::new(MessageDigest::sha256(), &private_key).unwrap();
- signer.update(signing_string.as_bytes()).unwrap();
- Ok(base64::encode(signer.sign_to_vec()?)) as Result<_, Error>
- })?
- .signature_header();
+ Ok(base64::encode(signer.sign_to_vec()?)) as Result<_, LemmyError>
+ },
+ )
+ .await?;
- Ok(signature_header_value)
+ Ok(digest_client)
}
-pub fn verify(request: &HttpRequest, actor: &dyn ActorType) -> Result<(), Error> {
- let headers = request
- .headers()
- .iter()
- .map(|h| -> Result<(String, String), Error> {
- Ok((h.0.as_str().to_owned(), h.1.to_str()?.to_owned()))
- })
- .collect::<Result<BTreeMap<String, String>, Error>>()?;
-
+pub fn verify(request: &HttpRequest, actor: &dyn ActorType) -> Result<(), LemmyError> {
let verified = HTTP_SIG_CONFIG
.begin_verify(
- request.method().as_str(),
- request.uri().path_and_query().unwrap().as_str(),
- headers,
+ request.method(),
+ request.uri().path_and_query(),
+ request.headers().clone(),
)?
- .verify(|signature, signing_string| -> Result<bool, Error> {
+ .verify(|signature, signing_string| -> Result<bool, LemmyError> {
debug!(
"Verifying with key {}, message {}",
&actor.public_key(),
debug!("verified signature for {}", &request.uri());
Ok(())
} else {
- Err(format_err!(
- "Invalid signature on request: {}",
- &request.uri()
- ))
+ Err(format_err!("Invalid signature on request: {}", &request.uri()).into())
}
}
use activitystreams::object::Note;
-use actix_web::Result;
+use actix_web::client::Client;
use diesel::{result::Error::NotFound, PgConnection};
-use failure::{Error, _core::fmt::Debug};
-use isahc::prelude::*;
use log::debug;
use serde::Deserialize;
-use std::time::Duration;
+use std::{fmt::Debug, time::Duration};
use url::Url;
use crate::{
api::site::SearchResponse,
+ blocking,
db::{
comment::{Comment, CommentForm},
comment_view::CommentView,
SearchType,
},
naive_now,
+ request::{retry, RecvError},
routes::nodeinfo::{NodeInfo, NodeInfoWellKnown},
+ DbPool,
+ LemmyError,
};
use crate::{
static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60;
// Fetch nodeinfo metadata from a remote instance.
-fn _fetch_node_info(domain: &str) -> Result<NodeInfo, Error> {
+async fn _fetch_node_info(client: &Client, domain: &str) -> Result<NodeInfo, LemmyError> {
let well_known_uri = Url::parse(&format!(
"{}://{}/.well-known/nodeinfo",
get_apub_protocol_string(),
domain
))?;
- let well_known = fetch_remote_object::<NodeInfoWellKnown>(&well_known_uri)?;
- Ok(fetch_remote_object::<NodeInfo>(&well_known.links.href)?)
+
+ let well_known = fetch_remote_object::<NodeInfoWellKnown>(client, &well_known_uri).await?;
+ let nodeinfo = fetch_remote_object::<NodeInfo>(client, &well_known.links.href).await?;
+
+ Ok(nodeinfo)
}
/// Fetch any type of ActivityPub object, handling things like HTTP headers, deserialisation,
/// timeouts etc.
-pub fn fetch_remote_object<Response>(url: &Url) -> Result<Response, Error>
+pub async fn fetch_remote_object<Response>(
+ client: &Client,
+ url: &Url,
+) -> Result<Response, LemmyError>
where
Response: for<'de> Deserialize<'de>,
{
if !is_apub_id_valid(&url) {
- return Err(format_err!("Activitypub uri invalid or blocked: {}", url));
+ return Err(format_err!("Activitypub uri invalid or blocked: {}", url).into());
}
- // TODO: this function should return a future
+
let timeout = Duration::from_secs(60);
- let text = Request::get(url.as_str())
- .header("Accept", APUB_JSON_CONTENT_TYPE)
- .connect_timeout(timeout)
- .timeout(timeout)
- .body(())?
- .send()?
- .text()?;
- let res: Response = serde_json::from_str(&text)?;
- Ok(res)
+
+ let json = retry(|| {
+ client
+ .get(url.as_str())
+ .header("Accept", APUB_JSON_CONTENT_TYPE)
+ .timeout(timeout)
+ .send()
+ })
+ .await?
+ .json()
+ .await
+ .map_err(|e| {
+ debug!("Receive error, {}", e);
+ RecvError(e.to_string())
+ })?;
+
+ Ok(json)
}
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
/// http://lemmy_alpha:8540/u/lemmy_alpha, or @lemmy_alpha@lemmy_alpha:8540
/// http://lemmy_alpha:8540/post/3
/// http://lemmy_alpha:8540/comment/2
-pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result<SearchResponse, Error> {
+pub async fn search_by_apub_id(
+ query: &str,
+ client: &Client,
+ pool: &DbPool,
+) -> Result<SearchResponse, LemmyError> {
// Parse the shorthand query url
let query_url = if query.contains('@') {
debug!("{}", query);
let split2 = split[0].split('!').collect::<Vec<&str>>();
(format!("/c/{}", split2[1]), split[1])
} else {
- return Err(format_err!("Invalid search query: {}", query));
+ return Err(format_err!("Invalid search query: {}", query).into());
}
} else {
- return Err(format_err!("Invalid search query: {}", query));
+ return Err(format_err!("Invalid search query: {}", query).into());
};
let url = format!("{}://{}{}", get_apub_protocol_string(), instance, name);
communities: vec![],
users: vec![],
};
- match fetch_remote_object::<SearchAcceptedObjects>(&query_url)? {
+
+ let response = match fetch_remote_object::<SearchAcceptedObjects>(client, &query_url).await? {
SearchAcceptedObjects::Person(p) => {
let user_uri = p.inner.object_props.get_id().unwrap().to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
- response.users = vec![UserView::read(conn, user.id)?];
+
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ response.users = vec![blocking(pool, move |conn| UserView::read(conn, user.id)).await??];
+
+ response
}
SearchAcceptedObjects::Group(g) => {
let community_uri = g.inner.object_props.get_id().unwrap().to_string();
- let community = get_or_fetch_and_upsert_remote_community(&community_uri, &conn)?;
+
+ let community =
+ get_or_fetch_and_upsert_remote_community(&community_uri, client, pool).await?;
+
// TODO Maybe at some point in the future, fetch all the history of a community
// fetch_community_outbox(&c, conn)?;
- response.communities = vec![CommunityView::read(conn, community.id, None)?];
+ response.communities = vec![
+ blocking(pool, move |conn| {
+ CommunityView::read(conn, community.id, None)
+ })
+ .await??,
+ ];
+
+ response
}
SearchAcceptedObjects::Page(p) => {
- let p = upsert_post(&PostForm::from_apub(&p, conn)?, conn)?;
- response.posts = vec![PostView::read(conn, p.id, None)?];
+ let post_form = PostForm::from_apub(&p, client, pool).await?;
+
+ let p = blocking(pool, move |conn| upsert_post(&post_form, conn)).await??;
+ response.posts = vec![blocking(pool, move |conn| PostView::read(conn, p.id, None)).await??];
+
+ response
}
SearchAcceptedObjects::Comment(c) => {
let post_url = c
.next()
.unwrap()
.to_string();
+
// TODO: also fetch parent comments if any
- let post = fetch_remote_object(&Url::parse(&post_url)?)?;
- upsert_post(&PostForm::from_apub(&post, conn)?, conn)?;
- let c = upsert_comment(&CommentForm::from_apub(&c, conn)?, conn)?;
- response.comments = vec![CommentView::read(conn, c.id, None)?];
+ let post = fetch_remote_object(client, &Url::parse(&post_url)?).await?;
+ let post_form = PostForm::from_apub(&post, client, pool).await?;
+ let comment_form = CommentForm::from_apub(&c, client, pool).await?;
+
+ blocking(pool, move |conn| upsert_post(&post_form, conn)).await??;
+ let c = blocking(pool, move |conn| upsert_comment(&comment_form, conn)).await??;
+ response.comments =
+ vec![blocking(pool, move |conn| CommentView::read(conn, c.id, None)).await??];
+
+ response
}
- }
+ };
+
Ok(response)
}
/// Check if a remote user exists, create if not found, if its too old update it.Fetch a user, insert/update it in the database and return the user.
-pub fn get_or_fetch_and_upsert_remote_user(
+pub async fn get_or_fetch_and_upsert_remote_user(
apub_id: &str,
- conn: &PgConnection,
-) -> Result<User_, Error> {
- match User_::read_from_actor_id(&conn, &apub_id) {
- Ok(u) => {
- // If its older than a day, re-fetch it
- if !u.local && should_refetch_actor(u.last_refreshed_at) {
- debug!("Fetching and updating from remote user: {}", apub_id);
- let person = fetch_remote_object::<PersonExt>(&Url::parse(apub_id)?)?;
- let mut uf = UserForm::from_apub(&person, &conn)?;
- uf.last_refreshed_at = Some(naive_now());
- Ok(User_::update(&conn, u.id, &uf)?)
- } else {
- Ok(u)
- }
+ client: &Client,
+ pool: &DbPool,
+) -> Result<User_, LemmyError> {
+ let apub_id_owned = apub_id.to_owned();
+ let user = blocking(pool, move |conn| {
+ User_::read_from_actor_id(conn, &apub_id_owned)
+ })
+ .await?;
+
+ match user {
+ // If its older than a day, re-fetch it
+ Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => {
+ debug!("Fetching and updating from remote user: {}", apub_id);
+ let person = fetch_remote_object::<PersonExt>(client, &Url::parse(apub_id)?).await?;
+
+ let mut uf = UserForm::from_apub(&person, client, pool).await?;
+ uf.last_refreshed_at = Some(naive_now());
+ let user = blocking(pool, move |conn| User_::update(conn, u.id, &uf)).await??;
+
+ Ok(user)
}
+ Ok(u) => Ok(u),
Err(NotFound {}) => {
debug!("Fetching and creating remote user: {}", apub_id);
- let person = fetch_remote_object::<PersonExt>(&Url::parse(apub_id)?)?;
- let uf = UserForm::from_apub(&person, &conn)?;
- Ok(User_::create(conn, &uf)?)
+ let person = fetch_remote_object::<PersonExt>(client, &Url::parse(apub_id)?).await?;
+
+ let uf = UserForm::from_apub(&person, client, pool).await?;
+ let user = blocking(pool, move |conn| User_::create(conn, &uf)).await??;
+
+ Ok(user)
}
- Err(e) => Err(Error::from(e)),
+ Err(e) => Err(e.into()),
}
}
}
/// Check if a remote community exists, create if not found, if its too old update it.Fetch a community, insert/update it in the database and return the community.
-pub fn get_or_fetch_and_upsert_remote_community(
+pub async fn get_or_fetch_and_upsert_remote_community(
apub_id: &str,
- conn: &PgConnection,
-) -> Result<Community, Error> {
- match Community::read_from_actor_id(&conn, &apub_id) {
- Ok(c) => {
- if !c.local && should_refetch_actor(c.last_refreshed_at) {
- debug!("Fetching and updating from remote community: {}", apub_id);
- let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
- let mut cf = CommunityForm::from_apub(&group, conn)?;
- cf.last_refreshed_at = Some(naive_now());
- Ok(Community::update(&conn, c.id, &cf)?)
- } else {
- Ok(c)
- }
+ client: &Client,
+ pool: &DbPool,
+) -> Result<Community, LemmyError> {
+ let apub_id_owned = apub_id.to_owned();
+ let community = blocking(pool, move |conn| {
+ Community::read_from_actor_id(conn, &apub_id_owned)
+ })
+ .await?;
+
+ match community {
+ Ok(c) if !c.local && should_refetch_actor(c.last_refreshed_at) => {
+ debug!("Fetching and updating from remote community: {}", apub_id);
+ let group = fetch_remote_object::<GroupExt>(client, &Url::parse(apub_id)?).await?;
+
+ let mut cf = CommunityForm::from_apub(&group, client, pool).await?;
+ cf.last_refreshed_at = Some(naive_now());
+ let community = blocking(pool, move |conn| Community::update(conn, c.id, &cf)).await??;
+
+ Ok(community)
}
+ Ok(c) => Ok(c),
Err(NotFound {}) => {
debug!("Fetching and creating remote community: {}", apub_id);
- let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
- let cf = CommunityForm::from_apub(&group, conn)?;
- let community = Community::create(conn, &cf)?;
+ let group = fetch_remote_object::<GroupExt>(client, &Url::parse(apub_id)?).await?;
+
+ let cf = CommunityForm::from_apub(&group, client, pool).await?;
+ let community = blocking(pool, move |conn| Community::create(conn, &cf)).await??;
// Also add the community moderators too
let creator_and_moderator_uris = group
.object_props
.get_many_attributed_to_xsd_any_uris()
.unwrap();
- let creator_and_moderators = creator_and_moderator_uris
- .map(|c| get_or_fetch_and_upsert_remote_user(&c.to_string(), &conn).unwrap())
- .collect::<Vec<User_>>();
-
- for mod_ in creator_and_moderators {
- let community_moderator_form = CommunityModeratorForm {
- community_id: community.id,
- user_id: mod_.id,
- };
- CommunityModerator::join(&conn, &community_moderator_form)?;
+
+ let mut creator_and_moderators = Vec::new();
+
+ for uri in creator_and_moderator_uris {
+ let c_or_m = get_or_fetch_and_upsert_remote_user(uri.as_str(), client, pool).await?;
+
+ creator_and_moderators.push(c_or_m);
}
+ let community_id = community.id;
+ blocking(pool, move |conn| {
+ for mod_ in creator_and_moderators {
+ let community_moderator_form = CommunityModeratorForm {
+ community_id,
+ user_id: mod_.id,
+ };
+
+ CommunityModerator::join(conn, &community_moderator_form)?;
+ }
+ Ok(()) as Result<(), LemmyError>
+ })
+ .await??;
+
Ok(community)
}
- Err(e) => Err(Error::from(e)),
+ Err(e) => Err(e.into()),
}
}
-fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result<Post, Error> {
+fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result<Post, LemmyError> {
let existing = Post::read_from_apub_id(conn, &post_form.ap_id);
match existing {
Err(NotFound {}) => Ok(Post::create(conn, &post_form)?),
Ok(p) => Ok(Post::update(conn, p.id, &post_form)?),
- Err(e) => Err(Error::from(e)),
+ Err(e) => Err(e.into()),
}
}
-pub fn get_or_fetch_and_insert_remote_post(
+pub async fn get_or_fetch_and_insert_remote_post(
post_ap_id: &str,
- conn: &PgConnection,
-) -> Result<Post, Error> {
- match Post::read_from_apub_id(conn, post_ap_id) {
+ client: &Client,
+ pool: &DbPool,
+) -> Result<Post, LemmyError> {
+ let post_ap_id_owned = post_ap_id.to_owned();
+ let post = blocking(pool, move |conn| {
+ Post::read_from_apub_id(conn, &post_ap_id_owned)
+ })
+ .await?;
+
+ match post {
Ok(p) => Ok(p),
Err(NotFound {}) => {
debug!("Fetching and creating remote post: {}", post_ap_id);
- let post = fetch_remote_object::<PageExt>(&Url::parse(post_ap_id)?)?;
- let post_form = PostForm::from_apub(&post, conn)?;
- Ok(Post::create(conn, &post_form)?)
+ let post = fetch_remote_object::<PageExt>(client, &Url::parse(post_ap_id)?).await?;
+ let post_form = PostForm::from_apub(&post, client, pool).await?;
+
+ let post = blocking(pool, move |conn| Post::create(conn, &post_form)).await??;
+
+ Ok(post)
}
- Err(e) => Err(Error::from(e)),
+ Err(e) => Err(e.into()),
}
}
-fn upsert_comment(comment_form: &CommentForm, conn: &PgConnection) -> Result<Comment, Error> {
+fn upsert_comment(comment_form: &CommentForm, conn: &PgConnection) -> Result<Comment, LemmyError> {
let existing = Comment::read_from_apub_id(conn, &comment_form.ap_id);
match existing {
Err(NotFound {}) => Ok(Comment::create(conn, &comment_form)?),
Ok(p) => Ok(Comment::update(conn, p.id, &comment_form)?),
- Err(e) => Err(Error::from(e)),
+ Err(e) => Err(e.into()),
}
}
-pub fn get_or_fetch_and_insert_remote_comment(
+pub async fn get_or_fetch_and_insert_remote_comment(
comment_ap_id: &str,
- conn: &PgConnection,
-) -> Result<Comment, Error> {
- match Comment::read_from_apub_id(conn, comment_ap_id) {
+ client: &Client,
+ pool: &DbPool,
+) -> Result<Comment, LemmyError> {
+ let comment_ap_id_owned = comment_ap_id.to_owned();
+ let comment = blocking(pool, move |conn| {
+ Comment::read_from_apub_id(conn, &comment_ap_id_owned)
+ })
+ .await?;
+
+ match comment {
Ok(p) => Ok(p),
Err(NotFound {}) => {
debug!(
"Fetching and creating remote comment and its parents: {}",
comment_ap_id
);
- let comment = fetch_remote_object::<Note>(&Url::parse(comment_ap_id)?)?;
- let comment_form = CommentForm::from_apub(&comment, conn)?;
- Ok(Comment::create(conn, &comment_form)?)
+ let comment = fetch_remote_object::<Note>(client, &Url::parse(comment_ap_id)?).await?;
+ let comment_form = CommentForm::from_apub(&comment, client, pool).await?;
+
+ let comment = blocking(pool, move |conn| Comment::create(conn, &comment_form)).await??;
+
+ Ok(comment)
}
- Err(e) => Err(Error::from(e)),
+ Err(e) => Err(e.into()),
}
}
// maybe), is community and user actors
// and user actors
// Fetch all posts in the outbox of the given user, and insert them into the database.
-// fn fetch_community_outbox(community: &Community, conn: &PgConnection) -> Result<Vec<Post>, Error> {
+// fn fetch_community_outbox(community: &Community, conn: &PgConnection) -> Result<Vec<Post>, LemmyError> {
// let outbox_url = Url::parse(&community.get_outbox_url())?;
// let outbox = fetch_remote_object::<OrderedCollection>(&outbox_url)?;
// let items = outbox.collection_props.get_many_items_base_boxes();
// Ok(
// items
// .unwrap()
-// .map(|obox: &BaseBox| -> Result<PostForm, Error> {
+// .map(|obox: &BaseBox| -> Result<PostForm, LemmyError> {
// let page = obox.clone().to_concrete::<Page>()?;
// PostForm::from_page(&page, conn)
// })
// .map(|pf| upsert_post(&pf?, conn))
-// .collect::<Result<Vec<Post>, Error>>()?,
+// .collect::<Result<Vec<Post>, LemmyError>>()?,
// )
// }
},
convert_datetime,
db::user::User_,
+ request::{retry, RecvError},
routes::webfinger::WebFingerResponse,
+ DbPool,
+ LemmyError,
MentionData,
Settings,
};
};
use activitystreams_ext::{Ext1, Ext2, Ext3};
use activitystreams_new::{activity::Follow, object::Tombstone, prelude::*};
-use actix_web::{body::Body, HttpResponse, Result};
+use actix_web::{body::Body, client::Client, HttpResponse};
use chrono::NaiveDateTime;
-use diesel::PgConnection;
-use failure::Error;
-use isahc::prelude::*;
use log::debug;
use serde::Serialize;
use url::Url;
// Checks if the ID has a valid format, correct scheme, and is in the allowed instance list.
fn is_apub_id_valid(apub_id: &Url) -> bool {
+ debug!("Checking {}", apub_id);
if apub_id.scheme() != get_apub_protocol_string() {
+ debug!("invalid scheme: {:?}", apub_id.scheme());
return false;
}
.map(|d| d.to_string())
.collect();
match apub_id.domain() {
- Some(d) => allowed_instances.contains(&d.to_owned()),
- None => false,
+ Some(d) => {
+ let contains = allowed_instances.contains(&d.to_owned());
+
+ if !contains {
+ debug!("{} not in {:?}", d, allowed_instances);
+ }
+
+ contains
+ }
+ None => {
+ debug!("missing domain");
+ false
+ }
}
}
+#[async_trait::async_trait(?Send)]
pub trait ToApub {
type Response;
- fn to_apub(&self, conn: &PgConnection) -> Result<Self::Response, Error>;
- fn to_tombstone(&self) -> Result<Tombstone, Error>;
+ async fn to_apub(&self, pool: &DbPool) -> Result<Self::Response, LemmyError>;
+ fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
}
/// Updated is actually the deletion time
object_id: &str,
updated: Option<NaiveDateTime>,
former_type: String,
-) -> Result<Tombstone, Error> {
+) -> Result<Tombstone, LemmyError> {
if deleted {
if let Some(updated) = updated {
let mut tombstone = Tombstone::new();
tombstone.set_deleted(convert_datetime(updated).into());
Ok(tombstone)
} else {
- Err(format_err!(
- "Cant convert to tombstone because updated time was None."
- ))
+ Err(format_err!("Cant convert to tombstone because updated time was None.").into())
}
} else {
- Err(format_err!(
- "Cant convert object to tombstone if it wasnt deleted"
- ))
+ Err(format_err!("Cant convert object to tombstone if it wasnt deleted").into())
}
}
+#[async_trait::async_trait(?Send)]
pub trait FromApub {
type ApubType;
- fn from_apub(apub: &Self::ApubType, conn: &PgConnection) -> Result<Self, Error>
+ async fn from_apub(
+ apub: &Self::ApubType,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<Self, LemmyError>
where
Self: Sized;
}
+#[async_trait::async_trait(?Send)]
pub trait ApubObjectType {
- fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
+ async fn send_create(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_update(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_undo_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_undo_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
}
+#[async_trait::async_trait(?Send)]
pub trait ApubLikeableType {
- fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_dislike(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_undo_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
+ async fn send_like(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_dislike(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_undo_like(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
}
pub fn get_shared_inbox(actor_id: &str) -> String {
)
}
+#[async_trait::async_trait(?Send)]
pub trait ActorType {
fn actor_id(&self) -> String;
// These two have default impls, since currently a community can't follow anything,
// and a user can't be followed (yet)
#[allow(unused_variables)]
- fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
- fn send_unfollow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
+ async fn send_follow(
+ &self,
+ follow_actor_id: &str,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_unfollow(
+ &self,
+ follow_actor_id: &str,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
#[allow(unused_variables)]
- fn send_accept_follow(&self, follow: &Follow, conn: &PgConnection) -> Result<(), Error>;
-
- fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
-
- fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
- fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
+ async fn send_accept_follow(
+ &self,
+ follow: &Follow,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+
+ async fn send_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_undo_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+
+ async fn send_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
+ async fn send_undo_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError>;
/// For a given community, returns the inboxes of all followers.
- fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error>;
+ async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<String>, LemmyError>;
// TODO move these to the db rows
fn get_inbox_url(&self) -> String {
}
}
-pub fn fetch_webfinger_url(mention: &MentionData) -> Result<String, Error> {
+pub async fn fetch_webfinger_url(
+ mention: &MentionData,
+ client: &Client,
+) -> Result<String, LemmyError> {
let fetch_url = format!(
"{}://{}/.well-known/webfinger?resource=acct:{}@{}",
get_apub_protocol_string(),
mention.domain
);
debug!("Fetching webfinger url: {}", &fetch_url);
- let text = isahc::get(&fetch_url)?.text()?;
- let res: WebFingerResponse = serde_json::from_str(&text)?;
+
+ let mut response = retry(|| client.get(&fetch_url).send()).await?;
+
+ let res: WebFingerResponse = response
+ .json()
+ .await
+ .map_err(|e| RecvError(e.to_string()))?;
+
let link = res
.links
.iter()
link
.href
.to_owned()
- .ok_or_else(|| format_err!("No href found."))
+ .ok_or_else(|| format_err!("No href found.").into())
}
PageExt,
ToApub,
},
+ blocking,
convert_datetime,
db::{
community::Community,
Crud,
},
routes::DbPoolParam,
+ DbPool,
+ LemmyError,
Settings,
};
use activitystreams::{
};
use activitystreams_ext::Ext1;
use activitystreams_new::object::Tombstone;
-use actix_web::{body::Body, web::Path, HttpResponse, Result};
-use diesel::PgConnection;
-use failure::Error;
+use actix_web::{body::Body, client::Client, web, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
/// Return the post json over HTTP.
pub async fn get_apub_post(
- info: Path<PostQuery>,
+ info: web::Path<PostQuery>,
db: DbPoolParam,
-) -> Result<HttpResponse<Body>, Error> {
+) -> Result<HttpResponse<Body>, LemmyError> {
let id = info.post_id.parse::<i32>()?;
- let post = Post::read(&&db.get()?, id)?;
+ let post = blocking(&db, move |conn| Post::read(conn, id)).await??;
+
if !post.deleted {
- Ok(create_apub_response(&post.to_apub(&db.get().unwrap())?))
+ Ok(create_apub_response(&post.to_apub(&db).await?))
} else {
Ok(create_apub_tombstone_response(&post.to_tombstone()?))
}
}
+#[async_trait::async_trait(?Send)]
impl ToApub for Post {
type Response = PageExt;
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
- fn to_apub(&self, conn: &PgConnection) -> Result<PageExt, Error> {
+ async fn to_apub(&self, pool: &DbPool) -> Result<PageExt, LemmyError> {
let mut page = Page::default();
let oprops: &mut ObjectProperties = page.as_mut();
- let creator = User_::read(conn, self.creator_id)?;
- let community = Community::read(conn, self.community_id)?;
+
+ let creator_id = self.creator_id;
+ let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
oprops
// Not needed when the Post is embedded in a collection (like for community outbox)
Ok(Ext1::new(page, ext))
}
- fn to_tombstone(&self) -> Result<Tombstone, Error> {
+ fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
create_tombstone(
self.deleted,
&self.ap_id,
}
}
+#[async_trait::async_trait(?Send)]
impl FromApub for PostForm {
type ApubType = PageExt;
/// Parse an ActivityPub page received from another instance into a Lemmy post.
- fn from_apub(page: &PageExt, conn: &PgConnection) -> Result<PostForm, Error> {
+ async fn from_apub(
+ page: &PageExt,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<PostForm, LemmyError> {
let ext = &page.ext_one;
let oprops = &page.inner.object_props;
let creator_actor_id = &oprops.get_attributed_to_xsd_any_uri().unwrap().to_string();
- let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, &conn)?;
+
+ let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, client, pool).await?;
+
let community_actor_id = &oprops.get_to_xsd_any_uri().unwrap().to_string();
- let community = get_or_fetch_and_upsert_remote_community(&community_actor_id, &conn)?;
+
+ let community =
+ get_or_fetch_and_upsert_remote_community(&community_actor_id, client, pool).await?;
let thumbnail_url = match oprops.get_image_any_image() {
Some(any_image) => any_image
}
}
+#[async_trait::async_trait(?Send)]
impl ApubObjectType for Post {
/// Send out information about a newly created post, to the followers of the community.
- fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_create(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/create/{}", self.ap_id, uuid::Uuid::new_v4());
let mut create = Create::new();
send_activity_to_community(
creator,
- conn,
&community,
vec![community.get_shared_inbox_url()],
create,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
/// Send out information about an edited post, to the followers of the community.
- fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_update(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/update/{}", self.ap_id, uuid::Uuid::new_v4());
let mut update = Update::new();
send_activity_to_community(
creator,
- conn,
&community,
vec![community.get_shared_inbox_url()],
update,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
let mut delete = Delete::default();
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(BaseBox::from_concrete(page)?)?;
- let community = Community::read(conn, self.community_id)?;
-
send_activity_to_community(
creator,
- conn,
&community,
vec![community.get_shared_inbox_url()],
delete,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_undo_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
let mut delete = Delete::default();
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(delete)?;
- let community = Community::read(conn, self.community_id)?;
send_activity_to_community(
creator,
- conn,
&community,
vec![community.get_shared_inbox_url()],
undo,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
let mut remove = Remove::default();
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
.set_object_base_box(BaseBox::from_concrete(page)?)?;
- let community = Community::read(conn, self.community_id)?;
-
send_activity_to_community(
mod_,
- conn,
&community,
vec![community.get_shared_inbox_url()],
remove,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+
+ async fn send_undo_remove(
+ &self,
+ mod_: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
let mut remove = Remove::default();
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
.set_object_base_box(remove)?;
- let community = Community::read(conn, self.community_id)?;
send_activity_to_community(
mod_,
- conn,
&community,
vec![community.get_shared_inbox_url()],
undo,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
}
+#[async_trait::async_trait(?Send)]
impl ApubLikeableType for Post {
- fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_like(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4());
let mut like = Like::new();
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
like,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_dislike(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_dislike(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/dislike/{}", self.ap_id, uuid::Uuid::new_v4());
let mut dislike = Dislike::new();
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
dislike,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
- fn send_undo_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = self.to_apub(conn)?;
- let community = Community::read(conn, self.community_id)?;
+ async fn send_undo_like(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let page = self.to_apub(pool).await?;
+
+ let community_id = self.community_id;
+ let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
+
let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4());
let mut like = Like::new();
send_activity_to_community(
&creator,
- &conn,
&community,
vec![community.get_shared_inbox_url()],
undo,
- )?;
+ client,
+ pool,
+ )
+ .await?;
Ok(())
}
}
FromApub,
ToApub,
},
+ blocking,
convert_datetime,
db::{
activity::insert_activity,
user::User_,
Crud,
},
+ DbPool,
+ LemmyError,
};
use activitystreams::{
activity::{Create, Delete, Undo, Update},
object::{kind::NoteType, properties::ObjectProperties, Note},
};
use activitystreams_new::object::Tombstone;
-use actix_web::Result;
-use diesel::PgConnection;
-use failure::Error;
+use actix_web::client::Client;
+#[async_trait::async_trait(?Send)]
impl ToApub for PrivateMessage {
type Response = Note;
- fn to_apub(&self, conn: &PgConnection) -> Result<Note, Error> {
+ async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let mut private_message = Note::default();
let oprops: &mut ObjectProperties = private_message.as_mut();
- let creator = User_::read(&conn, self.creator_id)?;
- let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let creator_id = self.creator_id;
+ let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??;
+
+ let recipient_id = self.recipient_id;
+ let recipient = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
oprops
.set_context_xsd_any_uri(context())?
Ok(private_message)
}
- fn to_tombstone(&self) -> Result<Tombstone, Error> {
+ fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
create_tombstone(
self.deleted,
&self.ap_id,
}
}
+#[async_trait::async_trait(?Send)]
impl FromApub for PrivateMessageForm {
type ApubType = Note;
/// Parse an ActivityPub note received from another instance into a Lemmy Private message
- fn from_apub(note: &Note, conn: &PgConnection) -> Result<PrivateMessageForm, Error> {
+ async fn from_apub(
+ note: &Note,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<PrivateMessageForm, LemmyError> {
let oprops = ¬e.object_props;
let creator_actor_id = &oprops.get_attributed_to_xsd_any_uri().unwrap().to_string();
- let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, &conn)?;
+
+ let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, client, pool).await?;
+
let recipient_actor_id = &oprops.get_to_xsd_any_uri().unwrap().to_string();
- let recipient = get_or_fetch_and_upsert_remote_user(&recipient_actor_id, &conn)?;
+
+ let recipient = get_or_fetch_and_upsert_remote_user(&recipient_actor_id, client, pool).await?;
Ok(PrivateMessageForm {
creator_id: creator.id,
}
}
+#[async_trait::async_trait(?Send)]
impl ApubObjectType for PrivateMessage {
/// Send out information about a newly created private message
- fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(conn)?;
+ async fn send_create(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
let id = format!("{}/create/{}", self.ap_id, uuid::Uuid::new_v4());
- let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let recipient_id = self.recipient_id;
+ let recipient = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
let mut create = Create::new();
create
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(note)?;
- insert_activity(&conn, creator.id, &create, true)?;
+ insert_activity(creator.id, create.clone(), true, pool).await?;
- send_activity(&create, creator, vec![to])?;
+ send_activity(client, &create, creator, vec![to]).await?;
Ok(())
}
/// Send out information about an edited post, to the followers of the community.
- fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(conn)?;
+ async fn send_update(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
let id = format!("{}/update/{}", self.ap_id, uuid::Uuid::new_v4());
- let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let recipient_id = self.recipient_id;
+ let recipient = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
let mut update = Update::new();
update
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(note)?;
- insert_activity(&conn, creator.id, &update, true)?;
+ insert_activity(creator.id, update.clone(), true, pool).await?;
- send_activity(&update, creator, vec![to])?;
+ send_activity(client, &update, creator, vec![to]).await?;
Ok(())
}
- fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(conn)?;
+ async fn send_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
- let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let recipient_id = self.recipient_id;
+ let recipient = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
let mut delete = Delete::new();
delete
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(note)?;
- insert_activity(&conn, creator.id, &delete, true)?;
+ insert_activity(creator.id, delete.clone(), true, pool).await?;
- send_activity(&delete, creator, vec![to])?;
+ send_activity(client, &delete, creator, vec![to]).await?;
Ok(())
}
- fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let note = self.to_apub(conn)?;
+ async fn send_undo_delete(
+ &self,
+ creator: &User_,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
+ let note = self.to_apub(pool).await?;
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
- let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let recipient_id = self.recipient_id;
+ let recipient = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
let mut delete = Delete::new();
delete
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(delete)?;
- insert_activity(&conn, creator.id, &undo, true)?;
+ insert_activity(creator.id, undo.clone(), true, pool).await?;
- send_activity(&undo, creator, vec![to])?;
+ send_activity(client, &undo, creator, vec![to]).await?;
Ok(())
}
- fn send_remove(&self, _mod_: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_remove(
+ &self,
+ _mod_: &User_,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
- fn send_undo_remove(&self, _mod_: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_undo_remove(
+ &self,
+ _mod_: &User_,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
}
GroupExt,
PageExt,
},
+ blocking,
db::{
activity::insert_activity,
comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
server::{SendComment, SendCommunityRoomMessage, SendPost},
UserOperation,
},
+ DbPool,
+ LemmyError,
};
use activitystreams::{
activity::{Announce, Create, Delete, Dislike, Like, Remove, Undo, Update},
Base,
BaseBox,
};
-use actix_web::{web, HttpRequest, HttpResponse, Result};
-use diesel::PgConnection;
-use failure::{Error, _core::fmt::Debug};
+use actix_web::{client::Client, web, HttpRequest, HttpResponse};
use log::debug;
use serde::{Deserialize, Serialize};
+use std::fmt::Debug;
#[serde(untagged)]
#[derive(Serialize, Deserialize, Debug)]
pub async fn shared_inbox(
request: HttpRequest,
input: web::Json<SharedAcceptedObjects>,
- db: DbPoolParam,
+ client: web::Data<Client>,
+ pool: DbPoolParam,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let activity = input.into_inner();
- let conn = &db.get().unwrap();
+ let pool = &pool;
+ let client = &client;
let json = serde_json::to_string(&activity)?;
debug!("Shared inbox received activity: {}", json);
let to = cc.replace("/followers", "");
// TODO: this is ugly
- match get_or_fetch_and_upsert_remote_user(&sender.to_string(), &conn) {
- Ok(u) => verify(&request, &u),
+ match get_or_fetch_and_upsert_remote_user(&sender.to_string(), &client, pool).await {
+ Ok(u) => verify(&request, &u)?,
Err(_) => {
- let c = get_or_fetch_and_upsert_remote_community(&sender.to_string(), &conn)?;
- verify(&request, &c)
+ let c = get_or_fetch_and_upsert_remote_community(&sender.to_string(), &client, pool).await?;
+ verify(&request, &c)?;
}
- }?;
+ }
match (activity, object.kind()) {
(SharedAcceptedObjects::Create(c), Some("Page")) => {
- receive_create_post(&c, &conn, chat_server)?;
- announce_activity_if_valid::<Create>(*c, &to, sender, conn)
+ receive_create_post((*c).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Create>(*c, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Update(u), Some("Page")) => {
- receive_update_post(&u, &conn, chat_server)?;
- announce_activity_if_valid::<Update>(*u, &to, sender, conn)
+ receive_update_post((*u).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Update>(*u, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Like(l), Some("Page")) => {
- receive_like_post(&l, &conn, chat_server)?;
- announce_activity_if_valid::<Like>(*l, &to, sender, conn)
+ receive_like_post((*l).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Like>(*l, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Dislike(d), Some("Page")) => {
- receive_dislike_post(&d, &conn, chat_server)?;
- announce_activity_if_valid::<Dislike>(*d, &to, sender, conn)
+ receive_dislike_post((*d).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Dislike>(*d, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Delete(d), Some("Page")) => {
- receive_delete_post(&d, &conn, chat_server)?;
- announce_activity_if_valid::<Delete>(*d, &to, sender, conn)
+ receive_delete_post((*d).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Delete>(*d, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Remove(r), Some("Page")) => {
- receive_remove_post(&r, &conn, chat_server)?;
- announce_activity_if_valid::<Remove>(*r, &to, sender, conn)
+ receive_remove_post((*r).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Remove>(*r, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Create(c), Some("Note")) => {
- receive_create_comment(&c, &conn, chat_server)?;
- announce_activity_if_valid::<Create>(*c, &to, sender, conn)
+ receive_create_comment((*c).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Create>(*c, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Update(u), Some("Note")) => {
- receive_update_comment(&u, &conn, chat_server)?;
- announce_activity_if_valid::<Update>(*u, &to, sender, conn)
+ receive_update_comment((*u).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Update>(*u, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Like(l), Some("Note")) => {
- receive_like_comment(&l, &conn, chat_server)?;
- announce_activity_if_valid::<Like>(*l, &to, sender, conn)
+ receive_like_comment((*l).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Like>(*l, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Dislike(d), Some("Note")) => {
- receive_dislike_comment(&d, &conn, chat_server)?;
- announce_activity_if_valid::<Dislike>(*d, &to, sender, conn)
+ receive_dislike_comment((*d).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Dislike>(*d, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Delete(d), Some("Note")) => {
- receive_delete_comment(&d, &conn, chat_server)?;
- announce_activity_if_valid::<Delete>(*d, &to, sender, conn)
+ receive_delete_comment((*d).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Delete>(*d, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Remove(r), Some("Note")) => {
- receive_remove_comment(&r, &conn, chat_server)?;
- announce_activity_if_valid::<Remove>(*r, &to, sender, conn)
+ receive_remove_comment((*r).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Remove>(*r, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Delete(d), Some("Group")) => {
- receive_delete_community(&d, &conn, chat_server)?;
- announce_activity_if_valid::<Delete>(*d, &to, sender, conn)
+ receive_delete_community((*d).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Delete>(*d, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Remove(r), Some("Group")) => {
- receive_remove_community(&r, &conn, chat_server)?;
- announce_activity_if_valid::<Remove>(*r, &to, sender, conn)
+ receive_remove_community((*r).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Remove>(*r, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Undo(u), Some("Delete")) => {
- receive_undo_delete(&u, &conn, chat_server)?;
- announce_activity_if_valid::<Undo>(*u, &to, sender, conn)
+ receive_undo_delete((*u).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Undo>(*u, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Undo(u), Some("Remove")) => {
- receive_undo_remove(&u, &conn, chat_server)?;
- announce_activity_if_valid::<Undo>(*u, &to, sender, conn)
+ receive_undo_remove((*u).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Undo>(*u, &to, sender, client, pool).await
}
(SharedAcceptedObjects::Undo(u), Some("Like")) => {
- receive_undo_like(&u, &conn, chat_server)?;
- announce_activity_if_valid::<Undo>(*u, &to, sender, conn)
+ receive_undo_like((*u).clone(), client, pool, chat_server).await?;
+ announce_activity_if_valid::<Undo>(*u, &to, sender, client, pool).await
}
- (SharedAcceptedObjects::Announce(a), _) => receive_announce(a, &conn, chat_server),
+ (SharedAcceptedObjects::Announce(a), _) => receive_announce(a, client, pool, chat_server).await,
(a, _) => receive_unhandled_activity(a),
}
}
// TODO: should pass in sender as ActorType, but thats a bit tricky in shared_inbox()
-fn announce_activity_if_valid<A>(
+async fn announce_activity_if_valid<A>(
activity: A,
community_uri: &str,
sender: &str,
- conn: &PgConnection,
-) -> Result<HttpResponse, Error>
+ client: &Client,
+ pool: &DbPool,
+) -> Result<HttpResponse, LemmyError>
where
A: Activity + Base + Serialize + Debug,
{
- let community = Community::read_from_actor_id(conn, &community_uri)?;
+ let community_uri = community_uri.to_owned();
+ let community = blocking(pool, move |conn| {
+ Community::read_from_actor_id(conn, &community_uri)
+ })
+ .await??;
+
if community.local {
- let sending_user = get_or_fetch_and_upsert_remote_user(&sender.to_string(), &conn)?;
- Community::do_announce(activity, &community, &sending_user, conn)
+ let sending_user = get_or_fetch_and_upsert_remote_user(sender, client, pool).await?;
+
+ Community::do_announce(activity, &community, &sending_user, client, pool).await
} else {
Ok(HttpResponse::NotFound().finish())
}
}
-fn receive_announce(
+async fn receive_announce(
announce: Box<Announce>,
- conn: &PgConnection,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let object = announce
.announce_props
.get_object_base_box()
let create = object.into_concrete::<Create>()?;
let inner_object = create.create_props.get_object_base_box().unwrap();
match inner_object.kind() {
- Some("Page") => receive_create_post(&create, &conn, chat_server),
- Some("Note") => receive_create_comment(&create, &conn, chat_server),
+ Some("Page") => receive_create_post(create, client, pool, chat_server).await,
+ Some("Note") => receive_create_comment(create, client, pool, chat_server).await,
_ => receive_unhandled_activity(announce),
}
}
let update = object.into_concrete::<Update>()?;
let inner_object = update.update_props.get_object_base_box().unwrap();
match inner_object.kind() {
- Some("Page") => receive_update_post(&update, &conn, chat_server),
- Some("Note") => receive_update_comment(&update, &conn, chat_server),
+ Some("Page") => receive_update_post(update, client, pool, chat_server).await,
+ Some("Note") => receive_update_comment(update, client, pool, chat_server).await,
_ => receive_unhandled_activity(announce),
}
}
let like = object.into_concrete::<Like>()?;
let inner_object = like.like_props.get_object_base_box().unwrap();
match inner_object.kind() {
- Some("Page") => receive_like_post(&like, &conn, chat_server),
- Some("Note") => receive_like_comment(&like, &conn, chat_server),
+ Some("Page") => receive_like_post(like, client, pool, chat_server).await,
+ Some("Note") => receive_like_comment(like, client, pool, chat_server).await,
_ => receive_unhandled_activity(announce),
}
}
let dislike = object.into_concrete::<Dislike>()?;
let inner_object = dislike.dislike_props.get_object_base_box().unwrap();
match inner_object.kind() {
- Some("Page") => receive_dislike_post(&dislike, &conn, chat_server),
- Some("Note") => receive_dislike_comment(&dislike, &conn, chat_server),
+ Some("Page") => receive_dislike_post(dislike, client, pool, chat_server).await,
+ Some("Note") => receive_dislike_comment(dislike, client, pool, chat_server).await,
_ => receive_unhandled_activity(announce),
}
}
let delete = object.into_concrete::<Delete>()?;
let inner_object = delete.delete_props.get_object_base_box().unwrap();
match inner_object.kind() {
- Some("Page") => receive_delete_post(&delete, &conn, chat_server),
- Some("Note") => receive_delete_comment(&delete, &conn, chat_server),
+ Some("Page") => receive_delete_post(delete, client, pool, chat_server).await,
+ Some("Note") => receive_delete_comment(delete, client, pool, chat_server).await,
_ => receive_unhandled_activity(announce),
}
}
let remove = object.into_concrete::<Remove>()?;
let inner_object = remove.remove_props.get_object_base_box().unwrap();
match inner_object.kind() {
- Some("Page") => receive_remove_post(&remove, &conn, chat_server),
- Some("Note") => receive_remove_comment(&remove, &conn, chat_server),
+ Some("Page") => receive_remove_post(remove, client, pool, chat_server).await,
+ Some("Note") => receive_remove_comment(remove, client, pool, chat_server).await,
_ => receive_unhandled_activity(announce),
}
}
let undo = object.into_concrete::<Undo>()?;
let inner_object = undo.undo_props.get_object_base_box().unwrap();
match inner_object.kind() {
- Some("Delete") => receive_undo_delete(&undo, &conn, chat_server),
- Some("Remove") => receive_undo_remove(&undo, &conn, chat_server),
- Some("Like") => receive_undo_like(&undo, &conn, chat_server),
+ Some("Delete") => receive_undo_delete(undo, client, pool, chat_server).await,
+ Some("Remove") => receive_undo_remove(undo, client, pool, chat_server).await,
+ Some("Like") => receive_undo_like(undo, client, pool, chat_server).await,
_ => receive_unhandled_activity(announce),
}
}
}
}
-fn receive_unhandled_activity<A>(activity: A) -> Result<HttpResponse, Error>
+fn receive_unhandled_activity<A>(activity: A) -> Result<HttpResponse, LemmyError>
where
A: Debug,
{
Ok(HttpResponse::NotImplemented().finish())
}
-fn receive_create_post(
- create: &Create,
- conn: &PgConnection,
+async fn receive_create_post(
+ create: Create,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let page = create
.create_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, create, false, pool).await?;
- insert_activity(&conn, user.id, &create, false)?;
+ let post = PostForm::from_apub(&page, client, pool).await?;
- let post = PostForm::from_apub(&page, &conn)?;
- let inserted_post = Post::create(conn, &post)?;
+ let inserted_post = blocking(pool, move |conn| Post::create(conn, &post)).await??;
// Refetch the view
- let post_view = PostView::read(&conn, inserted_post.id, None)?;
+ let inserted_post_id = inserted_post.id;
+ let post_view = blocking(pool, move |conn| {
+ PostView::read(conn, inserted_post_id, None)
+ })
+ .await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_create_comment(
- create: &Create,
- conn: &PgConnection,
+async fn receive_create_comment(
+ create: Create,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = create
.create_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
- insert_activity(&conn, user.id, &create, false)?;
+ insert_activity(user.id, create, false, pool).await?;
- let comment = CommentForm::from_apub(¬e, &conn)?;
- let inserted_comment = Comment::create(conn, &comment)?;
- let post = Post::read(&conn, inserted_comment.post_id)?;
+ let comment = CommentForm::from_apub(¬e, client, pool).await?;
+
+ let inserted_comment = blocking(pool, move |conn| Comment::create(conn, &comment)).await??;
+
+ let post_id = inserted_comment.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
// Note:
// Although mentions could be gotten from the post tags (they are included there), or the ccs,
// Its much easier to scrape them from the comment body, since the API has to do that
// anyway.
let mentions = scrape_text_for_mentions(&inserted_comment.content);
- let recipient_ids = send_local_notifs(&conn, &mentions, &inserted_comment, &user, &post);
+ let recipient_ids =
+ send_local_notifs(mentions, inserted_comment.clone(), user, post, pool).await?;
// Refetch the view
- let comment_view = CommentView::read(&conn, inserted_comment.id, None)?;
+ let comment_view = blocking(pool, move |conn| {
+ CommentView::read(conn, inserted_comment.id, None)
+ })
+ .await??;
let res = CommentResponse {
comment: comment_view,
Ok(HttpResponse::Ok().finish())
}
-fn receive_update_post(
- update: &Update,
- conn: &PgConnection,
+async fn receive_update_post(
+ update: Update,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let page = update
.update_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, update, false, pool).await?;
+
+ let post = PostForm::from_apub(&page, client, pool).await?;
- insert_activity(&conn, user.id, &update, false)?;
+ let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, client, pool)
+ .await?
+ .id;
- let post = PostForm::from_apub(&page, conn)?;
- let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, &conn)?.id;
- Post::update(conn, post_id, &post)?;
+ blocking(pool, move |conn| Post::update(conn, post_id, &post)).await??;
// Refetch the view
- let post_view = PostView::read(&conn, post_id, None)?;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_like_post(
- like: &Like,
-
- conn: &PgConnection,
+async fn receive_like_post(
+ like: Like,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let page = like
.like_props
.get_object_base_box()
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, like, false, pool).await?;
- insert_activity(&conn, user.id, &like, false)?;
+ let post = PostForm::from_apub(&page, client, pool).await?;
- let post = PostForm::from_apub(&page, conn)?;
- let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, &conn)?.id;
+ let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, client, pool)
+ .await?
+ .id;
let like_form = PostLikeForm {
post_id,
user_id: user.id,
score: 1,
};
- PostLike::remove(&conn, &like_form)?;
- PostLike::like(&conn, &like_form)?;
+ blocking(pool, move |conn| {
+ PostLike::remove(conn, &like_form)?;
+ PostLike::like(conn, &like_form)
+ })
+ .await??;
// Refetch the view
- let post_view = PostView::read(&conn, post_id, None)?;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_dislike_post(
- dislike: &Dislike,
-
- conn: &PgConnection,
+async fn receive_dislike_post(
+ dislike: Dislike,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let page = dislike
.dislike_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, dislike, false, pool).await?;
- insert_activity(&conn, user.id, &dislike, false)?;
+ let post = PostForm::from_apub(&page, client, pool).await?;
- let post = PostForm::from_apub(&page, conn)?;
- let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, &conn)?.id;
+ let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, client, pool)
+ .await?
+ .id;
let like_form = PostLikeForm {
post_id,
user_id: user.id,
score: -1,
};
- PostLike::remove(&conn, &like_form)?;
- PostLike::like(&conn, &like_form)?;
+ blocking(pool, move |conn| {
+ PostLike::remove(conn, &like_form)?;
+ PostLike::like(conn, &like_form)
+ })
+ .await??;
// Refetch the view
- let post_view = PostView::read(&conn, post_id, None)?;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_update_comment(
- update: &Update,
-
- conn: &PgConnection,
+async fn receive_update_comment(
+ update: Update,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = update
.update_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, update, false, pool).await?;
+
+ let comment = CommentForm::from_apub(¬e, client, pool).await?;
+
+ let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, client, pool)
+ .await?
+ .id;
- insert_activity(&conn, user.id, &update, false)?;
+ let updated_comment = blocking(pool, move |conn| {
+ Comment::update(conn, comment_id, &comment)
+ })
+ .await??;
- let comment = CommentForm::from_apub(¬e, &conn)?;
- let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, &conn)?.id;
- let updated_comment = Comment::update(conn, comment_id, &comment)?;
- let post = Post::read(&conn, updated_comment.post_id)?;
+ let post_id = updated_comment.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let mentions = scrape_text_for_mentions(&updated_comment.content);
- let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post);
+ let recipient_ids = send_local_notifs(mentions, updated_comment, user, post, pool).await?;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment_id, None)?;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
let res = CommentResponse {
comment: comment_view,
Ok(HttpResponse::Ok().finish())
}
-fn receive_like_comment(
- like: &Like,
-
- conn: &PgConnection,
+async fn receive_like_comment(
+ like: Like,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = like
.like_props
.get_object_base_box()
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, like, false, pool).await?;
- insert_activity(&conn, user.id, &like, false)?;
+ let comment = CommentForm::from_apub(¬e, client, pool).await?;
+
+ let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, client, pool)
+ .await?
+ .id;
- let comment = CommentForm::from_apub(¬e, &conn)?;
- let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, &conn)?.id;
let like_form = CommentLikeForm {
comment_id,
post_id: comment.post_id,
user_id: user.id,
score: 1,
};
- CommentLike::remove(&conn, &like_form)?;
- CommentLike::like(&conn, &like_form)?;
+ blocking(pool, move |conn| {
+ CommentLike::remove(conn, &like_form)?;
+ CommentLike::like(conn, &like_form)
+ })
+ .await??;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment_id, None)?;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
// TODO get those recipient actor ids from somewhere
let recipient_ids = vec![];
Ok(HttpResponse::Ok().finish())
}
-fn receive_dislike_comment(
- dislike: &Dislike,
-
- conn: &PgConnection,
+async fn receive_dislike_comment(
+ dislike: Dislike,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = dislike
.dislike_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
- insert_activity(&conn, user.id, &dislike, false)?;
+ insert_activity(user.id, dislike, false, pool).await?;
+
+ let comment = CommentForm::from_apub(¬e, client, pool).await?;
+
+ let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, client, pool)
+ .await?
+ .id;
- let comment = CommentForm::from_apub(¬e, &conn)?;
- let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, &conn)?.id;
let like_form = CommentLikeForm {
comment_id,
post_id: comment.post_id,
user_id: user.id,
score: -1,
};
- CommentLike::remove(&conn, &like_form)?;
- CommentLike::like(&conn, &like_form)?;
+ blocking(pool, move |conn| {
+ CommentLike::remove(conn, &like_form)?;
+ CommentLike::like(conn, &like_form)
+ })
+ .await??;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment_id, None)?;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
// TODO get those recipient actor ids from somewhere
let recipient_ids = vec![];
Ok(HttpResponse::Ok().finish())
}
-fn receive_delete_community(
- delete: &Delete,
-
- conn: &PgConnection,
+async fn receive_delete_community(
+ delete: Delete,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let user_uri = delete
.delete_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<GroupExt>()?;
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, delete, false, pool).await?;
- insert_activity(&conn, user.id, &delete, false)?;
+ let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+ .await?
+ .actor_id;
- let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
- let community = Community::read_from_actor_id(conn, &community_actor_id)?;
+ let community = blocking(pool, move |conn| {
+ Community::read_from_actor_id(conn, &community_actor_id)
+ })
+ .await??;
let community_form = CommunityForm {
name: community.name.to_owned(),
last_refreshed_at: None,
};
- Community::update(&conn, community.id, &community_form)?;
+ let community_id = community.id;
+ blocking(pool, move |conn| {
+ Community::update(conn, community_id, &community_form)
+ })
+ .await??;
+ let community_id = community.id;
let res = CommunityResponse {
- community: CommunityView::read(&conn, community.id, None)?,
+ community: blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, None)
+ })
+ .await??,
};
+ let community_id = res.community.id;
+
chat_server.do_send(SendCommunityRoomMessage {
op: UserOperation::EditCommunity,
response: res,
- community_id: community.id,
+ community_id,
my_id: None,
});
Ok(HttpResponse::Ok().finish())
}
-fn receive_remove_community(
- remove: &Remove,
-
- conn: &PgConnection,
+async fn receive_remove_community(
+ remove: Remove,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let mod_uri = remove
.remove_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<GroupExt>()?;
- let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
+ let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, client, pool).await?;
- insert_activity(&conn, mod_.id, &remove, false)?;
+ insert_activity(mod_.id, remove, false, pool).await?;
- let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
- let community = Community::read_from_actor_id(conn, &community_actor_id)?;
+ let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+ .await?
+ .actor_id;
+
+ let community = blocking(pool, move |conn| {
+ Community::read_from_actor_id(conn, &community_actor_id)
+ })
+ .await??;
let community_form = CommunityForm {
name: community.name.to_owned(),
last_refreshed_at: None,
};
- Community::update(&conn, community.id, &community_form)?;
+ let community_id = community.id;
+ blocking(pool, move |conn| {
+ Community::update(conn, community_id, &community_form)
+ })
+ .await??;
+ let community_id = community.id;
let res = CommunityResponse {
- community: CommunityView::read(&conn, community.id, None)?,
+ community: blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, None)
+ })
+ .await??,
};
+ let community_id = res.community.id;
+
chat_server.do_send(SendCommunityRoomMessage {
op: UserOperation::EditCommunity,
response: res,
- community_id: community.id,
+ community_id,
my_id: None,
});
Ok(HttpResponse::Ok().finish())
}
-fn receive_delete_post(
- delete: &Delete,
-
- conn: &PgConnection,
+async fn receive_delete_post(
+ delete: Delete,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let user_uri = delete
.delete_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<PageExt>()?;
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, delete, false, pool).await?;
- insert_activity(&conn, user.id, &delete, false)?;
+ let post_ap_id = PostForm::from_apub(&page, client, pool).await?.ap_id;
- let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
- let post = get_or_fetch_and_insert_remote_post(&post_ap_id, &conn)?;
+ let post = get_or_fetch_and_insert_remote_post(&post_ap_id, client, pool).await?;
let post_form = PostForm {
name: post.name.to_owned(),
local: post.local,
published: None,
};
- Post::update(&conn, post.id, &post_form)?;
+ let post_id = post.id;
+ blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
// Refetch the view
- let post_view = PostView::read(&conn, post.id, None)?;
+ let post_id = post.id;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_remove_post(
- remove: &Remove,
-
- conn: &PgConnection,
+async fn receive_remove_post(
+ remove: Remove,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let mod_uri = remove
.remove_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<PageExt>()?;
- let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
+ let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, client, pool).await?;
- insert_activity(&conn, mod_.id, &remove, false)?;
+ insert_activity(mod_.id, remove, false, pool).await?;
- let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
- let post = get_or_fetch_and_insert_remote_post(&post_ap_id, &conn)?;
+ let post_ap_id = PostForm::from_apub(&page, client, pool).await?.ap_id;
+
+ let post = get_or_fetch_and_insert_remote_post(&post_ap_id, client, pool).await?;
let post_form = PostForm {
name: post.name.to_owned(),
local: post.local,
published: None,
};
- Post::update(&conn, post.id, &post_form)?;
+ let post_id = post.id;
+ blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
// Refetch the view
- let post_view = PostView::read(&conn, post.id, None)?;
+ let post_id = post.id;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_delete_comment(
- delete: &Delete,
-
- conn: &PgConnection,
+async fn receive_delete_comment(
+ delete: Delete,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let user_uri = delete
.delete_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<Note>()?;
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, delete, false, pool).await?;
+
+ let comment_ap_id = CommentForm::from_apub(¬e, client, pool).await?.ap_id;
- insert_activity(&conn, user.id, &delete, false)?;
+ let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, client, pool).await?;
- let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
- let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, &conn)?;
let comment_form = CommentForm {
content: comment.content.to_owned(),
parent_id: comment.parent_id,
ap_id: comment.ap_id,
local: comment.local,
};
- Comment::update(&conn, comment.id, &comment_form)?;
+ let comment_id = comment.id;
+ blocking(pool, move |conn| {
+ Comment::update(conn, comment_id, &comment_form)
+ })
+ .await??;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment.id, None)?;
+ let comment_id = comment.id;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
// TODO get those recipient actor ids from somewhere
let recipient_ids = vec![];
Ok(HttpResponse::Ok().finish())
}
-fn receive_remove_comment(
- remove: &Remove,
-
- conn: &PgConnection,
+async fn receive_remove_comment(
+ remove: Remove,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let mod_uri = remove
.remove_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<Note>()?;
- let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
+ let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, client, pool).await?;
+
+ insert_activity(mod_.id, remove, false, pool).await?;
- insert_activity(&conn, mod_.id, &remove, false)?;
+ let comment_ap_id = CommentForm::from_apub(¬e, client, pool).await?.ap_id;
+
+ let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, client, pool).await?;
- let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
- let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, &conn)?;
let comment_form = CommentForm {
content: comment.content.to_owned(),
parent_id: comment.parent_id,
ap_id: comment.ap_id,
local: comment.local,
};
- Comment::update(&conn, comment.id, &comment_form)?;
+ let comment_id = comment.id;
+ blocking(pool, move |conn| {
+ Comment::update(conn, comment_id, &comment_form)
+ })
+ .await??;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment.id, None)?;
+ let comment_id = comment.id;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
// TODO get those recipient actor ids from somewhere
let recipient_ids = vec![];
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_delete(
- undo: &Undo,
-
- conn: &PgConnection,
+async fn receive_undo_delete(
+ undo: Undo,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let delete = undo
.undo_props
.get_object_base_box()
.unwrap();
match type_ {
- "Note" => receive_undo_delete_comment(&delete, &conn, chat_server),
- "Page" => receive_undo_delete_post(&delete, &conn, chat_server),
- "Group" => receive_undo_delete_community(&delete, &conn, chat_server),
- d => Err(format_err!("Undo Delete type {} not supported", d)),
+ "Note" => receive_undo_delete_comment(delete, client, pool, chat_server).await,
+ "Page" => receive_undo_delete_post(delete, client, pool, chat_server).await,
+ "Group" => receive_undo_delete_community(delete, client, pool, chat_server).await,
+ d => Err(format_err!("Undo Delete type {} not supported", d).into()),
}
}
-fn receive_undo_remove(
- undo: &Undo,
-
- conn: &PgConnection,
+async fn receive_undo_remove(
+ undo: Undo,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let remove = undo
.undo_props
.get_object_base_box()
.unwrap();
match type_ {
- "Note" => receive_undo_remove_comment(&remove, &conn, chat_server),
- "Page" => receive_undo_remove_post(&remove, &conn, chat_server),
- "Group" => receive_undo_remove_community(&remove, &conn, chat_server),
- d => Err(format_err!("Undo Delete type {} not supported", d)),
+ "Note" => receive_undo_remove_comment(remove, client, pool, chat_server).await,
+ "Page" => receive_undo_remove_post(remove, client, pool, chat_server).await,
+ "Group" => receive_undo_remove_community(remove, client, pool, chat_server).await,
+ d => Err(format_err!("Undo Delete type {} not supported", d).into()),
}
}
-fn receive_undo_delete_comment(
- delete: &Delete,
-
- conn: &PgConnection,
+async fn receive_undo_delete_comment(
+ delete: Delete,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let user_uri = delete
.delete_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<Note>()?;
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, delete, false, pool).await?;
+
+ let comment_ap_id = CommentForm::from_apub(¬e, client, pool).await?.ap_id;
- insert_activity(&conn, user.id, &delete, false)?;
+ let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, client, pool).await?;
- let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
- let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, &conn)?;
let comment_form = CommentForm {
content: comment.content.to_owned(),
parent_id: comment.parent_id,
ap_id: comment.ap_id,
local: comment.local,
};
- Comment::update(&conn, comment.id, &comment_form)?;
+ let comment_id = comment.id;
+ blocking(pool, move |conn| {
+ Comment::update(conn, comment_id, &comment_form)
+ })
+ .await??;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment.id, None)?;
+ let comment_id = comment.id;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
// TODO get those recipient actor ids from somewhere
let recipient_ids = vec![];
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_remove_comment(
- remove: &Remove,
-
- conn: &PgConnection,
+async fn receive_undo_remove_comment(
+ remove: Remove,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let mod_uri = remove
.remove_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<Note>()?;
- let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
+ let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, client, pool).await?;
+
+ insert_activity(mod_.id, remove, false, pool).await?;
- insert_activity(&conn, mod_.id, &remove, false)?;
+ let comment_ap_id = CommentForm::from_apub(¬e, client, pool).await?.ap_id;
+
+ let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, client, pool).await?;
- let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
- let comment = get_or_fetch_and_insert_remote_comment(&comment_ap_id, &conn)?;
let comment_form = CommentForm {
content: comment.content.to_owned(),
parent_id: comment.parent_id,
ap_id: comment.ap_id,
local: comment.local,
};
- Comment::update(&conn, comment.id, &comment_form)?;
+ let comment_id = comment.id;
+ blocking(pool, move |conn| {
+ Comment::update(conn, comment_id, &comment_form)
+ })
+ .await??;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment.id, None)?;
+ let comment_id = comment.id;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
// TODO get those recipient actor ids from somewhere
let recipient_ids = vec![];
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_delete_post(
- delete: &Delete,
-
- conn: &PgConnection,
+async fn receive_undo_delete_post(
+ delete: Delete,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let user_uri = delete
.delete_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<PageExt>()?;
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
- insert_activity(&conn, user.id, &delete, false)?;
+ insert_activity(user.id, delete, false, pool).await?;
- let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
- let post = get_or_fetch_and_insert_remote_post(&post_ap_id, &conn)?;
+ let post_ap_id = PostForm::from_apub(&page, client, pool).await?.ap_id;
+
+ let post = get_or_fetch_and_insert_remote_post(&post_ap_id, client, pool).await?;
let post_form = PostForm {
name: post.name.to_owned(),
local: post.local,
published: None,
};
- Post::update(&conn, post.id, &post_form)?;
+ let post_id = post.id;
+ blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
// Refetch the view
- let post_view = PostView::read(&conn, post.id, None)?;
+ let post_id = post.id;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_remove_post(
- remove: &Remove,
-
- conn: &PgConnection,
+async fn receive_undo_remove_post(
+ remove: Remove,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let mod_uri = remove
.remove_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<PageExt>()?;
- let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
+ let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, client, pool).await?;
- insert_activity(&conn, mod_.id, &remove, false)?;
+ insert_activity(mod_.id, remove, false, pool).await?;
- let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
- let post = get_or_fetch_and_insert_remote_post(&post_ap_id, &conn)?;
+ let post_ap_id = PostForm::from_apub(&page, client, pool).await?.ap_id;
+
+ let post = get_or_fetch_and_insert_remote_post(&post_ap_id, client, pool).await?;
let post_form = PostForm {
name: post.name.to_owned(),
local: post.local,
published: None,
};
- Post::update(&conn, post.id, &post_form)?;
+ let post_id = post.id;
+ blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
// Refetch the view
- let post_view = PostView::read(&conn, post.id, None)?;
+ let post_id = post.id;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_delete_community(
- delete: &Delete,
-
- conn: &PgConnection,
+async fn receive_undo_delete_community(
+ delete: Delete,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let user_uri = delete
.delete_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<GroupExt>()?;
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
- insert_activity(&conn, user.id, &delete, false)?;
+ insert_activity(user.id, delete, false, pool).await?;
- let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
- let community = Community::read_from_actor_id(conn, &community_actor_id)?;
+ let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+ .await?
+ .actor_id;
+
+ let community = blocking(pool, move |conn| {
+ Community::read_from_actor_id(conn, &community_actor_id)
+ })
+ .await??;
let community_form = CommunityForm {
name: community.name.to_owned(),
last_refreshed_at: None,
};
- Community::update(&conn, community.id, &community_form)?;
+ let community_id = community.id;
+ blocking(pool, move |conn| {
+ Community::update(conn, community_id, &community_form)
+ })
+ .await??;
+ let community_id = community.id;
let res = CommunityResponse {
- community: CommunityView::read(&conn, community.id, None)?,
+ community: blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, None)
+ })
+ .await??,
};
+ let community_id = res.community.id;
+
chat_server.do_send(SendCommunityRoomMessage {
op: UserOperation::EditCommunity,
response: res,
- community_id: community.id,
+ community_id,
my_id: None,
});
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_remove_community(
- remove: &Remove,
-
- conn: &PgConnection,
+async fn receive_undo_remove_community(
+ remove: Remove,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let mod_uri = remove
.remove_props
.get_actor_xsd_any_uri()
.to_owned()
.into_concrete::<GroupExt>()?;
- let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
+ let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, client, pool).await?;
+
+ insert_activity(mod_.id, remove, false, pool).await?;
- insert_activity(&conn, mod_.id, &remove, false)?;
+ let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+ .await?
+ .actor_id;
- let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
- let community = Community::read_from_actor_id(conn, &community_actor_id)?;
+ let community = blocking(pool, move |conn| {
+ Community::read_from_actor_id(conn, &community_actor_id)
+ })
+ .await??;
let community_form = CommunityForm {
name: community.name.to_owned(),
last_refreshed_at: None,
};
- Community::update(&conn, community.id, &community_form)?;
+ let community_id = community.id;
+ blocking(pool, move |conn| {
+ Community::update(conn, community_id, &community_form)
+ })
+ .await??;
+ let community_id = community.id;
let res = CommunityResponse {
- community: CommunityView::read(&conn, community.id, None)?,
+ community: blocking(pool, move |conn| {
+ CommunityView::read(conn, community_id, None)
+ })
+ .await??,
};
+ let community_id = res.community.id;
+
chat_server.do_send(SendCommunityRoomMessage {
op: UserOperation::EditCommunity,
response: res,
- community_id: community.id,
+ community_id,
my_id: None,
});
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_like(
- undo: &Undo,
-
- conn: &PgConnection,
+async fn receive_undo_like(
+ undo: Undo,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let like = undo
.undo_props
.get_object_base_box()
.unwrap();
match type_ {
- "Note" => receive_undo_like_comment(&like, &conn, chat_server),
- "Page" => receive_undo_like_post(&like, &conn, chat_server),
- d => Err(format_err!("Undo Delete type {} not supported", d)),
+ "Note" => receive_undo_like_comment(like, client, pool, chat_server).await,
+ "Page" => receive_undo_like_post(like, client, pool, chat_server).await,
+ d => Err(format_err!("Undo Delete type {} not supported", d).into()),
}
}
-fn receive_undo_like_comment(
- like: &Like,
-
- conn: &PgConnection,
+async fn receive_undo_like_comment(
+ like: Like,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = like
.like_props
.get_object_base_box()
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, like, false, pool).await?;
- insert_activity(&conn, user.id, &like, false)?;
+ let comment = CommentForm::from_apub(¬e, client, pool).await?;
+
+ let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, client, pool)
+ .await?
+ .id;
- let comment = CommentForm::from_apub(¬e, &conn)?;
- let comment_id = get_or_fetch_and_insert_remote_comment(&comment.ap_id, &conn)?.id;
let like_form = CommentLikeForm {
comment_id,
post_id: comment.post_id,
user_id: user.id,
score: 0,
};
- CommentLike::remove(&conn, &like_form)?;
+ blocking(pool, move |conn| CommentLike::remove(conn, &like_form)).await??;
// Refetch the view
- let comment_view = CommentView::read(&conn, comment_id, None)?;
+ let comment_view =
+ blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
// TODO get those recipient actor ids from somewhere
let recipient_ids = vec![];
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_like_post(
- like: &Like,
-
- conn: &PgConnection,
+async fn receive_undo_like_post(
+ like: Like,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let page = like
.like_props
.get_object_base_box()
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
+
+ insert_activity(user.id, like, false, pool).await?;
- insert_activity(&conn, user.id, &like, false)?;
+ let post = PostForm::from_apub(&page, client, pool).await?;
- let post = PostForm::from_apub(&page, conn)?;
- let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, &conn)?.id;
+ let post_id = get_or_fetch_and_insert_remote_post(&post.ap_id, client, pool)
+ .await?
+ .id;
let like_form = PostLikeForm {
post_id,
user_id: user.id,
score: 1,
};
- PostLike::remove(&conn, &like_form)?;
+ blocking(pool, move |conn| PostLike::remove(conn, &like_form)).await??;
// Refetch the view
- let post_view = PostView::read(&conn, post_id, None)?;
+ let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
let res = PostResponse { post: post_view };
PersonExt,
ToApub,
},
+ blocking,
convert_datetime,
db::{
activity::insert_activity,
},
naive_now,
routes::DbPoolParam,
+ DbPool,
+ LemmyError,
};
use activitystreams::{
actor::{properties::ApActorProperties, Person},
object::Tombstone,
prelude::*,
};
-use actix_web::{body::Body, web::Path, HttpResponse, Result};
-use diesel::PgConnection;
-use failure::Error;
+use actix_web::{body::Body, client::Client, web, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
user_name: String,
}
+#[async_trait::async_trait(?Send)]
impl ToApub for User_ {
type Response = PersonExt;
// Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
- fn to_apub(&self, _conn: &PgConnection) -> Result<PersonExt, Error> {
+ async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
// TODO go through all these to_string and to_owned()
let mut person = Person::default();
let oprops: &mut ObjectProperties = person.as_mut();
Ok(Ext2::new(person, actor_props, self.get_public_key_ext()))
}
- fn to_tombstone(&self) -> Result<Tombstone, Error> {
+ fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
unimplemented!()
}
}
+#[async_trait::async_trait(?Send)]
impl ActorType for User_ {
fn actor_id(&self) -> String {
self.actor_id.to_owned()
}
/// As a given local user, send out a follow request to a remote community.
- fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error> {
+ async fn send_follow(
+ &self,
+ follow_actor_id: &str,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
follow.set_context(context()).set_id(id.parse()?);
let to = format!("{}/inbox", follow_actor_id);
- insert_activity(&conn, self.id, &follow, true)?;
+ insert_activity(self.id, follow.clone(), true, pool).await?;
- send_activity(&follow, self, vec![to])?;
+ send_activity(client, &follow, self, vec![to]).await?;
Ok(())
}
- fn send_unfollow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error> {
+ async fn send_unfollow(
+ &self,
+ follow_actor_id: &str,
+ client: &Client,
+ pool: &DbPool,
+ ) -> Result<(), LemmyError> {
let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
follow.set_context(context()).set_id(id.parse()?);
let mut undo = Undo::new(self.actor_id.parse::<XsdAnyUri>()?, follow.into_any_base()?);
undo.set_context(context()).set_id(undo_id.parse()?);
- insert_activity(&conn, self.id, &undo, true)?;
+ insert_activity(self.id, undo.clone(), true, pool).await?;
- send_activity(&undo, self, vec![to])?;
+ send_activity(client, &undo, self, vec![to]).await?;
Ok(())
}
- fn send_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_delete(
+ &self,
+ _creator: &User_,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
- fn send_undo_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_undo_delete(
+ &self,
+ _creator: &User_,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
- fn send_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_remove(
+ &self,
+ _creator: &User_,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
- fn send_undo_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_undo_remove(
+ &self,
+ _creator: &User_,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
- fn send_accept_follow(&self, _follow: &Follow, _conn: &PgConnection) -> Result<(), Error> {
+ async fn send_accept_follow(
+ &self,
+ _follow: &Follow,
+ _client: &Client,
+ _pool: &DbPool,
+ ) -> Result<(), LemmyError> {
unimplemented!()
}
- fn get_follower_inboxes(&self, _conn: &PgConnection) -> Result<Vec<String>, Error> {
+ async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<String>, LemmyError> {
unimplemented!()
}
}
+#[async_trait::async_trait(?Send)]
impl FromApub for UserForm {
type ApubType = PersonExt;
/// Parse an ActivityPub person received from another instance into a Lemmy user.
- fn from_apub(person: &PersonExt, _conn: &PgConnection) -> Result<Self, Error> {
+ async fn from_apub(person: &PersonExt, _: &Client, _: &DbPool) -> Result<Self, LemmyError> {
let oprops = &person.inner.object_props;
let aprops = &person.ext_one;
let public_key: &PublicKey = &person.ext_two.public_key;
/// Return the user json over HTTP.
pub async fn get_apub_user_http(
- info: Path<UserQuery>,
+ info: web::Path<UserQuery>,
db: DbPoolParam,
-) -> Result<HttpResponse<Body>, Error> {
- let user = User_::find_by_email_or_username(&&db.get()?, &info.user_name)?;
- let u = user.to_apub(&db.get().unwrap())?;
+) -> Result<HttpResponse<Body>, LemmyError> {
+ let user_name = info.into_inner().user_name;
+ let user = blocking(&db, move |conn| {
+ User_::find_by_email_or_username(conn, &user_name)
+ })
+ .await??;
+ let u = user.to_apub(&db).await?;
Ok(create_apub_response(&u))
}
fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user},
FromApub,
},
+ blocking,
db::{
activity::insert_activity,
community::{CommunityFollower, CommunityFollowerForm},
naive_now,
routes::{ChatServerParam, DbPoolParam},
websocket::{server::SendUserRoomMessage, UserOperation},
+ DbPool,
+ LemmyError,
};
use activitystreams::{
activity::{Accept, Create, Delete, Undo, Update},
object::Note,
};
-use actix_web::{web, HttpRequest, HttpResponse, Result};
-use diesel::PgConnection;
-use failure::{Error, _core::fmt::Debug};
+use actix_web::{client::Client, web, HttpRequest, HttpResponse};
use log::debug;
use serde::Deserialize;
+use std::fmt::Debug;
#[serde(untagged)]
#[derive(Deserialize, Debug)]
request: HttpRequest,
input: web::Json<UserAcceptedObjects>,
path: web::Path<String>,
+ client: web::Data<Client>,
db: DbPoolParam,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
// TODO: would be nice if we could do the signature check here, but we cant access the actor property
let input = input.into_inner();
- let conn = &db.get().unwrap();
let username = path.into_inner();
debug!("User {} received activity: {:?}", &username, &input);
match input {
- UserAcceptedObjects::Accept(a) => receive_accept(&a, &request, &username, &conn),
+ UserAcceptedObjects::Accept(a) => receive_accept(*a, &request, &username, &client, &db).await,
UserAcceptedObjects::Create(c) => {
- receive_create_private_message(&c, &request, &conn, chat_server)
+ receive_create_private_message(*c, &request, &client, &db, chat_server).await
}
UserAcceptedObjects::Update(u) => {
- receive_update_private_message(&u, &request, &conn, chat_server)
+ receive_update_private_message(*u, &request, &client, &db, chat_server).await
}
UserAcceptedObjects::Delete(d) => {
- receive_delete_private_message(&d, &request, &conn, chat_server)
+ receive_delete_private_message(*d, &request, &client, &db, chat_server).await
}
UserAcceptedObjects::Undo(u) => {
- receive_undo_delete_private_message(&u, &request, &conn, chat_server)
+ receive_undo_delete_private_message(*u, &request, &client, &db, chat_server).await
}
}
}
/// Handle accepted follows.
-fn receive_accept(
- accept: &Accept,
+async fn receive_accept(
+ accept: Accept,
request: &HttpRequest,
username: &str,
- conn: &PgConnection,
-) -> Result<HttpResponse, Error> {
+ client: &Client,
+ pool: &DbPool,
+) -> Result<HttpResponse, LemmyError> {
let community_uri = accept
.accept_props
.get_actor_xsd_any_uri()
.unwrap()
.to_string();
- let community = get_or_fetch_and_upsert_remote_community(&community_uri, conn)?;
+ let community = get_or_fetch_and_upsert_remote_community(&community_uri, client, pool).await?;
verify(request, &community)?;
- let user = User_::read_from_name(&conn, username)?;
+ let username = username.to_owned();
+ let user = blocking(pool, move |conn| User_::read_from_name(conn, &username)).await??;
- insert_activity(&conn, community.creator_id, &accept, false)?;
+ insert_activity(community.creator_id, accept, false, pool).await?;
// Now you need to add this to the community follower
let community_follower_form = CommunityFollowerForm {
};
// This will fail if they're already a follower
- CommunityFollower::follow(&conn, &community_follower_form)?;
+ blocking(pool, move |conn| {
+ CommunityFollower::follow(conn, &community_follower_form)
+ })
+ .await??;
// TODO: make sure that we actually requested a follow
Ok(HttpResponse::Ok().finish())
}
-fn receive_create_private_message(
- create: &Create,
+async fn receive_create_private_message(
+ create: Create,
request: &HttpRequest,
- conn: &PgConnection,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = create
.create_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
verify(request, &user)?;
- insert_activity(&conn, user.id, &create, false)?;
+ insert_activity(user.id, create, false, pool).await?;
- let private_message = PrivateMessageForm::from_apub(¬e, &conn)?;
- let inserted_private_message = PrivateMessage::create(&conn, &private_message)?;
+ let private_message = PrivateMessageForm::from_apub(¬e, client, pool).await?;
- let message = PrivateMessageView::read(&conn, inserted_private_message.id)?;
+ let inserted_private_message = blocking(pool, move |conn| {
+ PrivateMessage::create(conn, &private_message)
+ })
+ .await??;
- let res = PrivateMessageResponse {
- message: message.to_owned(),
- };
+ let message = blocking(pool, move |conn| {
+ PrivateMessageView::read(conn, inserted_private_message.id)
+ })
+ .await??;
+
+ let res = PrivateMessageResponse { message };
+
+ let recipient_id = res.message.recipient_id;
chat_server.do_send(SendUserRoomMessage {
op: UserOperation::CreatePrivateMessage,
response: res,
- recipient_id: message.recipient_id,
+ recipient_id,
my_id: None,
});
Ok(HttpResponse::Ok().finish())
}
-fn receive_update_private_message(
- update: &Update,
+async fn receive_update_private_message(
+ update: Update,
request: &HttpRequest,
- conn: &PgConnection,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = update
.update_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
verify(request, &user)?;
- insert_activity(&conn, user.id, &update, false)?;
+ insert_activity(user.id, update, false, pool).await?;
- let private_message = PrivateMessageForm::from_apub(¬e, &conn)?;
- let private_message_id = PrivateMessage::read_from_apub_id(&conn, &private_message.ap_id)?.id;
- PrivateMessage::update(conn, private_message_id, &private_message)?;
+ let private_message_form = PrivateMessageForm::from_apub(¬e, client, pool).await?;
- let message = PrivateMessageView::read(&conn, private_message_id)?;
+ let private_message_ap_id = private_message_form.ap_id.clone();
+ let private_message = blocking(pool, move |conn| {
+ PrivateMessage::read_from_apub_id(conn, &private_message_ap_id)
+ })
+ .await??;
- let res = PrivateMessageResponse {
- message: message.to_owned(),
- };
+ let private_message_id = private_message.id;
+ blocking(pool, move |conn| {
+ PrivateMessage::update(conn, private_message_id, &private_message_form)
+ })
+ .await??;
+
+ let private_message_id = private_message.id;
+ let message = blocking(pool, move |conn| {
+ PrivateMessageView::read(conn, private_message_id)
+ })
+ .await??;
+
+ let res = PrivateMessageResponse { message };
+
+ let recipient_id = res.message.recipient_id;
chat_server.do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage,
response: res,
- recipient_id: message.recipient_id,
+ recipient_id,
my_id: None,
});
Ok(HttpResponse::Ok().finish())
}
-fn receive_delete_private_message(
- delete: &Delete,
+async fn receive_delete_private_message(
+ delete: Delete,
request: &HttpRequest,
- conn: &PgConnection,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let note = delete
.delete_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
verify(request, &user)?;
- insert_activity(&conn, user.id, &delete, false)?;
+ insert_activity(user.id, delete, false, pool).await?;
+
+ let private_message_form = PrivateMessageForm::from_apub(¬e, client, pool).await?;
+
+ let private_message_ap_id = private_message_form.ap_id;
+ let private_message = blocking(pool, move |conn| {
+ PrivateMessage::read_from_apub_id(conn, &private_message_ap_id)
+ })
+ .await??;
- let private_message = PrivateMessageForm::from_apub(¬e, &conn)?;
- let private_message_id = PrivateMessage::read_from_apub_id(&conn, &private_message.ap_id)?.id;
let private_message_form = PrivateMessageForm {
- content: private_message.content,
+ content: private_message_form.content,
recipient_id: private_message.recipient_id,
creator_id: private_message.creator_id,
deleted: Some(true),
published: None,
updated: Some(naive_now()),
};
- PrivateMessage::update(conn, private_message_id, &private_message_form)?;
- let message = PrivateMessageView::read(&conn, private_message_id)?;
+ let private_message_id = private_message.id;
+ blocking(pool, move |conn| {
+ PrivateMessage::update(conn, private_message_id, &private_message_form)
+ })
+ .await??;
- let res = PrivateMessageResponse {
- message: message.to_owned(),
- };
+ let private_message_id = private_message.id;
+ let message = blocking(pool, move |conn| {
+ PrivateMessageView::read(&conn, private_message_id)
+ })
+ .await??;
+
+ let res = PrivateMessageResponse { message };
+
+ let recipient_id = res.message.recipient_id;
chat_server.do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage,
response: res,
- recipient_id: message.recipient_id,
+ recipient_id,
my_id: None,
});
Ok(HttpResponse::Ok().finish())
}
-fn receive_undo_delete_private_message(
- undo: &Undo,
+async fn receive_undo_delete_private_message(
+ undo: Undo,
request: &HttpRequest,
- conn: &PgConnection,
+ client: &Client,
+ pool: &DbPool,
chat_server: ChatServerParam,
-) -> Result<HttpResponse, Error> {
+) -> Result<HttpResponse, LemmyError> {
let delete = undo
.undo_props
.get_object_base_box()
.unwrap()
.to_string();
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, client, pool).await?;
verify(request, &user)?;
- insert_activity(&conn, user.id, &delete, false)?;
+ insert_activity(user.id, delete, false, pool).await?;
+
+ let private_message = PrivateMessageForm::from_apub(¬e, client, pool).await?;
+
+ let private_message_ap_id = private_message.ap_id.clone();
+ let private_message_id = blocking(pool, move |conn| {
+ PrivateMessage::read_from_apub_id(conn, &private_message_ap_id).map(|pm| pm.id)
+ })
+ .await??;
- let private_message = PrivateMessageForm::from_apub(¬e, &conn)?;
- let private_message_id = PrivateMessage::read_from_apub_id(&conn, &private_message.ap_id)?.id;
let private_message_form = PrivateMessageForm {
content: private_message.content,
recipient_id: private_message.recipient_id,
published: None,
updated: Some(naive_now()),
};
- PrivateMessage::update(conn, private_message_id, &private_message_form)?;
- let message = PrivateMessageView::read(&conn, private_message_id)?;
+ blocking(pool, move |conn| {
+ PrivateMessage::update(conn, private_message_id, &private_message_form)
+ })
+ .await??;
- let res = PrivateMessageResponse {
- message: message.to_owned(),
- };
+ let message = blocking(pool, move |conn| {
+ PrivateMessageView::read(&conn, private_message_id)
+ })
+ .await??;
+
+ let res = PrivateMessageResponse { message };
+
+ let recipient_id = res.message.recipient_id;
chat_server.do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage,
response: res,
- recipient_id: message.recipient_id,
+ recipient_id,
my_id: None,
});
-use crate::{db::Crud, schema::activity};
+use crate::{blocking, db::Crud, schema::activity, DbPool, LemmyError};
use diesel::{dsl::*, result::Error, *};
-use failure::_core::fmt::Debug;
use log::debug;
use serde::{Deserialize, Serialize};
use serde_json::Value;
+use std::fmt::Debug;
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
#[table_name = "activity"]
}
}
-pub fn insert_activity<T>(
+pub async fn insert_activity<T>(
+ user_id: i32,
+ data: T,
+ local: bool,
+ pool: &DbPool,
+) -> Result<(), LemmyError>
+where
+ T: Serialize + Debug + Send + 'static,
+{
+ blocking(pool, move |conn| {
+ do_insert_activity(conn, user_id, &data, local)
+ })
+ .await??;
+ Ok(())
+}
+
+fn do_insert_activity<T>(
conn: &PgConnection,
user_id: i32,
data: &T,
local: bool,
-) -> Result<(), failure::Error>
+) -> Result<(), LemmyError>
where
T: Serialize + Debug,
{
apub::{extensions::signatures::generate_actor_keypair, make_apub_endpoint, EndpointType},
db::Crud,
naive_now,
+ LemmyError,
};
use diesel::*;
-use failure::Error;
use log::info;
-pub fn run_advanced_migrations(conn: &PgConnection) -> Result<(), Error> {
+pub fn run_advanced_migrations(conn: &PgConnection) -> Result<(), LemmyError> {
user_updates_2020_04_02(&conn)?;
community_updates_2020_04_02(&conn)?;
post_updates_2020_04_03(&conn)?;
comment_updates_2020_04_03(&conn)?;
private_message_updates_2020_05_05(&conn)?;
+
Ok(())
}
-fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> {
+fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> {
use crate::schema::user_::dsl::*;
info!("Running user_updates_2020_04_02");
Ok(())
}
-fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> {
+fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> {
use crate::schema::community::dsl::*;
info!("Running community_updates_2020_04_02");
Ok(())
}
-fn post_updates_2020_04_03(conn: &PgConnection) -> Result<(), Error> {
+fn post_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> {
use crate::schema::post::dsl::*;
info!("Running post_updates_2020_04_03");
Ok(())
}
-fn comment_updates_2020_04_03(conn: &PgConnection) -> Result<(), Error> {
+fn comment_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> {
use crate::schema::comment::dsl::*;
info!("Running comment_updates_2020_04_03");
Ok(())
}
-fn private_message_updates_2020_05_05(conn: &PgConnection) -> Result<(), Error> {
+fn private_message_updates_2020_05_05(conn: &PgConnection) -> Result<(), LemmyError> {
use crate::schema::private_message::dsl::*;
info!("Running private_message_updates_2020_05_05");
// )
// SELECT * FROM MyTree;
-#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
#[belongs_to(Post)]
#[table_name = "comment"]
pub struct Comment {
use diesel::{dsl::*, result::Error, *};
use serde::{Deserialize, Serialize};
-#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
#[table_name = "community"]
pub struct Community {
pub id: i32,
impl PasswordResetRequest {
pub fn create_token(conn: &PgConnection, from_user_id: i32, token: &str) -> Result<Self, Error> {
let mut hasher = Sha256::new();
- hasher.input(token);
- let token_hash: String = PasswordResetRequest::bytes_to_hex(hasher.result().to_vec());
+ hasher.update(token);
+ let token_hash: String = PasswordResetRequest::bytes_to_hex(hasher.finalize().to_vec());
let form = PasswordResetRequestForm {
user_id: from_user_id,
}
pub fn read_from_token(conn: &PgConnection, token: &str) -> Result<Self, Error> {
let mut hasher = Sha256::new();
- hasher.input(token);
- let token_hash: String = PasswordResetRequest::bytes_to_hex(hasher.result().to_vec());
+ hasher.update(token);
+ let token_hash: String = PasswordResetRequest::bytes_to_hex(hasher.finalize().to_vec());
password_reset_request
.filter(token_encrypted.eq(token_hash))
.filter(published.gt(now - 1.days()))
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
use serde::{Deserialize, Serialize};
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
+#[derive(Clone, Queryable, Identifiable, PartialEq, Debug)]
#[table_name = "user_"]
pub struct User_ {
pub id: i32,
pub extern crate sha2;
pub extern crate strum;
+pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
+where
+ F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
+ T: Send + 'static,
+{
+ let pool = pool.clone();
+ let res = actix_web::web::block(move || {
+ let conn = pool.get()?;
+ let res = (f)(&conn);
+ Ok(res) as Result<_, LemmyError>
+ })
+ .await?;
+
+ Ok(res)
+}
+
pub mod api;
pub mod apub;
pub mod db;
pub mod rate_limit;
+pub mod request;
pub mod routes;
pub mod schema;
pub mod settings;
pub mod version;
pub mod websocket;
-use crate::settings::Settings;
-use actix_web::dev::ConnectionInfo;
+use crate::{
+ request::{retry, RecvError},
+ settings::Settings,
+};
+use actix_web::{client::Client, dev::ConnectionInfo};
use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, Utc};
-use isahc::prelude::*;
use itertools::Itertools;
use lettre::{
smtp::{
use regex::{Regex, RegexBuilder};
use serde::Deserialize;
+pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
pub type ConnectionId = usize;
pub type PostId = i32;
pub type CommunityId = i32;
pub type UserId = i32;
pub type IPAddr = String;
+#[derive(Debug)]
+pub struct LemmyError {
+ inner: failure::Error,
+}
+
+impl std::fmt::Display for LemmyError {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ self.inner.fmt(f)
+ }
+}
+
+impl actix_web::error::ResponseError for LemmyError {}
+
+impl<T> From<T> for LemmyError
+where
+ T: Into<failure::Error>,
+{
+ fn from(t: T) -> Self {
+ LemmyError { inner: t.into() }
+ }
+}
+
pub fn to_datetime_utc(ndt: NaiveDateTime) -> DateTime<Utc> {
DateTime::<Utc>::from_utc(ndt, Utc)
}
EMAIL_REGEX.is_match(test)
}
-pub fn is_image_content_type(test: &str) -> Result<(), failure::Error> {
- if isahc::get(test)?
+pub async fn is_image_content_type(client: &Client, test: &str) -> Result<(), LemmyError> {
+ let response = retry(|| client.get(test).send()).await?;
+
+ if response
.headers()
.get("Content-Type")
.ok_or_else(|| format_err!("No Content-Type header"))?
{
Ok(())
} else {
- Err(format_err!("Not an image type."))
+ Err(format_err!("Not an image type.").into())
}
}
html: Option<String>,
}
-pub fn fetch_iframely(url: &str) -> Result<IframelyResponse, failure::Error> {
+pub async fn fetch_iframely(client: &Client, url: &str) -> Result<IframelyResponse, LemmyError> {
let fetch_url = format!("http://iframely/oembed?url={}", url);
- let text = isahc::get(&fetch_url)?.text()?;
- let res: IframelyResponse = serde_json::from_str(&text)?;
+
+ let mut response = retry(|| client.get(&fetch_url).send()).await?;
+
+ let res: IframelyResponse = response
+ .json()
+ .await
+ .map_err(|e| RecvError(e.to_string()))?;
Ok(res)
}
delete_token: String,
}
-pub fn fetch_pictrs(image_url: &str) -> Result<PictrsResponse, failure::Error> {
- is_image_content_type(image_url)?;
+pub async fn fetch_pictrs(client: &Client, image_url: &str) -> Result<PictrsResponse, LemmyError> {
+ is_image_content_type(client, image_url).await?;
let fetch_url = format!(
"http://pictrs:8080/image/download?url={}",
utf8_percent_encode(image_url, NON_ALPHANUMERIC) // TODO this might not be needed
);
- let text = isahc::get(&fetch_url)?.text()?;
- let res: PictrsResponse = serde_json::from_str(&text)?;
- if res.msg == "ok" {
- Ok(res)
+
+ let mut response = retry(|| client.get(&fetch_url).send()).await?;
+
+ let response: PictrsResponse = response
+ .json()
+ .await
+ .map_err(|e| RecvError(e.to_string()))?;
+
+ if response.msg == "ok" {
+ Ok(response)
} else {
- Err(format_err!("{}", &res.msg))
+ Err(format_err!("{}", &response.msg).into())
}
}
-fn fetch_iframely_and_pictrs_data(
+async fn fetch_iframely_and_pictrs_data(
+ client: &Client,
url: Option<String>,
) -> (
Option<String>,
Some(url) => {
// Fetch iframely data
let (iframely_title, iframely_description, iframely_thumbnail_url, iframely_html) =
- match fetch_iframely(url) {
+ match fetch_iframely(client, url).await {
Ok(res) => (res.title, res.description, res.thumbnail_url, res.html),
Err(e) => {
error!("iframely err: {}", e);
// Fetch pictrs thumbnail
let pictrs_thumbnail = match iframely_thumbnail_url {
- Some(iframely_thumbnail_url) => match fetch_pictrs(&iframely_thumbnail_url) {
+ Some(iframely_thumbnail_url) => match fetch_pictrs(client, &iframely_thumbnail_url).await {
Ok(res) => Some(res.files[0].file.to_owned()),
Err(e) => {
error!("pictrs err: {}", e);
}
},
// Try to generate a small thumbnail if iframely is not supported
- None => match fetch_pictrs(&url) {
+ None => match fetch_pictrs(client, &url).await {
Ok(res) => Some(res.files[0].file.to_owned()),
Err(e) => {
error!("pictrs err: {}", e);
pub fn get_ip(conn_info: &ConnectionInfo) -> String {
conn_info
- .remote()
+ .remote_addr()
.unwrap_or("127.0.0.1:12345")
.split(':')
.next()
#[test]
fn test_mentions_regex() {
- let text = "Just read a great blog post by [@tedu@honk.teduangst.com](/u/test). And another by !test_community@fish.teduangst.com . Another [@lemmy@lemmy_alpha:8540](/u/fish)";
+ let text = "Just read a great blog post by [@tedu@honk.teduangst.com](/u/test). And another by !test_community@fish.teduangst.com . Another [@lemmy@lemmy-alpha:8540](/u/fish)";
let mentions = scrape_text_for_mentions(text);
assert_eq!(mentions[0].name, "tedu".to_string());
assert_eq!(mentions[0].domain, "honk.teduangst.com".to_string());
- assert_eq!(mentions[1].domain, "lemmy_alpha:8540".to_string());
+ assert_eq!(mentions[1].domain, "lemmy-alpha:8540".to_string());
}
#[test]
fn test_image() {
- assert!(is_image_content_type("https://1734811051.rsc.cdn77.org/data/images/full/365645/as-virus-kills-navajos-in-their-homes-tribal-women-provide-lifeline.jpg?w=600?w=650").is_ok());
- assert!(is_image_content_type(
- "https://twitter.com/BenjaminNorton/status/1259922424272957440?s=20"
- )
- .is_err());
+ actix_rt::System::new("tset_image").block_on(async move {
+ let client = actix_web::client::Client::default();
+ assert!(is_image_content_type(&client, "https://1734811051.rsc.cdn77.org/data/images/full/365645/as-virus-kills-navajos-in-their-homes-tribal-women-provide-lifeline.jpg?w=600?w=650").await.is_ok());
+ assert!(is_image_content_type(&client,
+ "https://twitter.com/BenjaminNorton/status/1259922424272957440?s=20"
+ )
+ .await.is_err()
+ );
+ });
}
#[test]
// These helped with testing
// #[test]
// fn test_iframely() {
- // let res = fetch_iframely("https://www.redspark.nu/?p=15341");
+ // let res = fetch_iframely(client, "https://www.redspark.nu/?p=15341").await;
// assert!(res.is_ok());
// }
#[macro_use]
pub extern crate lazy_static;
+pub type DbPool = Pool<ConnectionManager<PgConnection>>;
+
use crate::lemmy_server::actix_web::dev::Service;
use actix::prelude::*;
use actix_web::{
body::Body,
+ client::Client,
dev::{ServiceRequest, ServiceResponse},
http::{
header::{CACHE_CONTROL, CONTENT_TYPE},
PgConnection,
};
use lemmy_server::{
+ blocking,
db::code_migrations::run_advanced_migrations,
rate_limit::{rate_limiter::RateLimiter, RateLimit},
routes::{api, federation, feeds, index, nodeinfo, webfinger},
settings::Settings,
websocket::server::*,
+ LemmyError,
};
use regex::Regex;
-use std::{io, sync::Arc};
+use std::sync::Arc;
use tokio::sync::Mutex;
lazy_static! {
embed_migrations!();
#[actix_rt::main]
-async fn main() -> io::Result<()> {
+async fn main() -> Result<(), LemmyError> {
env_logger::init();
let settings = Settings::get();
.unwrap_or_else(|_| panic!("Error connecting to {}", settings.get_database_url()));
// Run the migrations from code
- let conn = pool.get().unwrap();
- embedded_migrations::run(&conn).unwrap();
- run_advanced_migrations(&conn).unwrap();
+ blocking(&pool, move |conn| {
+ embedded_migrations::run(conn)?;
+ run_advanced_migrations(conn)?;
+ Ok(()) as Result<(), LemmyError>
+ })
+ .await??;
// Set up the rate limiter
let rate_limiter = RateLimit {
};
// Set up websocket server
- let server = ChatServer::startup(pool.clone(), rate_limiter.clone()).start();
+ let server = ChatServer::startup(pool.clone(), rate_limiter.clone(), Client::default()).start();
println!(
"Starting http server at {}:{}",
.wrap(middleware::Logger::default())
.data(pool.clone())
.data(server.clone())
+ .data(Client::default())
// The routes
.configure(move |cfg| api::config(cfg, &rate_limiter))
.configure(federation::config)
})
.bind((settings.bind, settings.port))?
.run()
- .await
+ .await?;
+
+ Ok(())
}
fn add_cache_headers<S>(
use super::{IPAddr, Settings};
-use crate::{api::APIError, get_ip, settings::RateLimitConfig};
+use crate::{get_ip, settings::RateLimitConfig, LemmyError};
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use futures::future::{ok, Ready};
use rate_limiter::{RateLimitType, RateLimiter};
#[derive(Debug, Clone)]
pub struct RateLimit {
+ // it might be reasonable to use a std::sync::Mutex here, since we don't need to lock this
+ // across await points
pub rate_limiter: Arc<Mutex<RateLimiter>>,
}
fut: impl Future<Output = Result<T, E>>,
) -> Result<T, E>
where
- E: From<failure::Error>,
+ E: From<LemmyError>,
{
- let rate_limit: RateLimitConfig = actix_web::web::block(move || {
- // needs to be in a web::block because the RwLock in settings is from stdlib
- Ok(Settings::get().rate_limit) as Result<_, failure::Error>
- })
- .await
- .map_err(|e| match e {
- actix_web::error::BlockingError::Error(e) => e,
- _ => APIError::err("Operation canceled").into(),
- })?;
+ // Does not need to be blocking because the RwLock in settings never held across await points,
+ // and the operation here locks only long enough to clone
+ let rate_limit: RateLimitConfig = Settings::get().rate_limit;
// before
{
false,
)?;
+ drop(limiter);
return fut.await;
}
RateLimitType::Post => {
use super::IPAddr;
-use crate::api::APIError;
-use failure::Error;
+use crate::{api::APIError, LemmyError};
use log::debug;
use std::{collections::HashMap, time::SystemTime};
use strum::IntoEnumIterator;
rate: i32,
per: i32,
check_only: bool,
- ) -> Result<(), Error> {
+ ) -> Result<(), LemmyError> {
self.insert_ip(ip);
if let Some(bucket) = self.buckets.get_mut(&type_) {
if let Some(rate_limit) = bucket.get_mut(ip) {
--- /dev/null
+use crate::LemmyError;
+use std::future::Future;
+
+#[derive(Clone, Debug, Fail)]
+#[fail(display = "Error sending request, {}", _0)]
+struct SendError(pub String);
+
+#[derive(Clone, Debug, Fail)]
+#[fail(display = "Error receiving response, {}", _0)]
+pub struct RecvError(pub String);
+
+pub async fn retry<F, Fut, T>(f: F) -> Result<T, LemmyError>
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = Result<T, actix_web::client::SendRequestError>>,
+{
+ retry_custom(|| async { Ok((f)().await) }).await
+}
+
+pub async fn retry_custom<F, Fut, T>(f: F) -> Result<T, LemmyError>
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = Result<Result<T, actix_web::client::SendRequestError>, LemmyError>>,
+{
+ let mut response = Err(format_err!("connect timeout").into());
+
+ for _ in 0u8..3 {
+ match (f)().await? {
+ Ok(t) => return Ok(t),
+ Err(e) => {
+ if is_connect_timeout(&e) {
+ response = Err(SendError(e.to_string()).into());
+ continue;
+ }
+ return Err(SendError(e.to_string()).into());
+ }
+ }
+ }
+
+ response
+}
+
+fn is_connect_timeout(e: &actix_web::client::SendRequestError) -> bool {
+ if let actix_web::client::SendRequestError::Connect(e) = e {
+ if let actix_web::client::ConnectError::Timeout = e {
+ return true;
+ }
+ }
+
+ false
+}
routes::{ChatServerParam, DbPoolParam},
websocket::WebsocketInfo,
};
-use actix_web::{error::ErrorBadRequest, *};
+use actix_web::{client::Client, error::ErrorBadRequest, *};
use serde::Serialize;
pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
async fn perform<Request>(
data: Request,
+ client: &Client,
db: DbPoolParam,
chat_server: ChatServerParam,
) -> Result<HttpResponse, Error>
id: None,
};
- let oper: Oper<Request> = Oper::new(data);
+ let oper: Oper<Request> = Oper::new(data, client.clone());
- let res = web::block(move || oper.perform(db.get_ref().to_owned(), Some(ws_info)))
+ let res = oper
+ .perform(&db, Some(ws_info))
.await
.map(|json| HttpResponse::Ok().json(json))
.map_err(ErrorBadRequest)?;
async fn route_get<Data>(
data: web::Query<Data>,
+ client: web::Data<Client>,
db: DbPoolParam,
chat_server: ChatServerParam,
) -> Result<HttpResponse, Error>
Data: Serialize + Send + 'static,
Oper<Data>: Perform,
{
- perform::<Data>(data.0, db, chat_server).await
+ perform::<Data>(data.0, &client, db, chat_server).await
}
async fn route_post<Data>(
data: web::Json<Data>,
+ client: web::Data<Client>,
db: DbPoolParam,
chat_server: ChatServerParam,
) -> Result<HttpResponse, Error>
Data: Serialize + Send + 'static,
Oper<Data>: Perform,
{
- perform::<Data>(data.0, db, chat_server).await
+ perform::<Data>(data.0, &client, db, chat_server).await
}
use crate::{
+ blocking,
db::{
comment_view::{ReplyQueryBuilder, ReplyView},
community::Community,
markdown_to_html,
routes::DbPoolParam,
settings::Settings,
+ LemmyError,
};
use actix_web::{error::ErrorBadRequest, *};
use chrono::{DateTime, NaiveDateTime, Utc};
}
async fn get_all_feed(info: web::Query<Params>, db: DbPoolParam) -> Result<HttpResponse, Error> {
- let res = web::block(move || {
- let conn = db.get()?;
- get_feed_all_data(&conn, &get_sort_type(info)?)
- })
- .await
- .map(|rss| {
+ let sort_type = get_sort_type(info).map_err(ErrorBadRequest)?;
+
+ let rss = blocking(&db, move |conn| get_feed_all_data(conn, &sort_type))
+ .await?
+ .map_err(ErrorBadRequest)?;
+
+ Ok(
HttpResponse::Ok()
.content_type("application/rss+xml")
- .body(rss)
- })
- .map_err(ErrorBadRequest)?;
- Ok(res)
+ .body(rss),
+ )
}
-fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result<String, failure::Error> {
+fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result<String, LemmyError> {
let site_view = SiteView::read(&conn)?;
let posts = PostQueryBuilder::create(&conn)
info: web::Query<Params>,
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
) -> Result<HttpResponse, Error> {
- let res = web::block(move || {
- let conn = db.get()?;
-
- let sort_type = get_sort_type(info)?;
-
- let request_type = match path.0.as_ref() {
- "u" => RequestType::User,
- "c" => RequestType::Community,
- "front" => RequestType::Front,
- "inbox" => RequestType::Inbox,
- _ => return Err(format_err!("wrong_type")),
- };
-
- let param = path.1.to_owned();
-
- match request_type {
- RequestType::User => get_feed_user(&conn, &sort_type, param),
- RequestType::Community => get_feed_community(&conn, &sort_type, param),
- RequestType::Front => get_feed_front(&conn, &sort_type, param),
- RequestType::Inbox => get_feed_inbox(&conn, param),
- }
+ let sort_type = get_sort_type(info).map_err(ErrorBadRequest)?;
+
+ let request_type = match path.0.as_ref() {
+ "u" => RequestType::User,
+ "c" => RequestType::Community,
+ "front" => RequestType::Front,
+ "inbox" => RequestType::Inbox,
+ _ => return Err(ErrorBadRequest(LemmyError::from(format_err!("wrong_type")))),
+ };
+
+ let param = path.1.to_owned();
+
+ let builder = blocking(&db, move |conn| match request_type {
+ RequestType::User => get_feed_user(conn, &sort_type, param),
+ RequestType::Community => get_feed_community(conn, &sort_type, param),
+ RequestType::Front => get_feed_front(conn, &sort_type, param),
+ RequestType::Inbox => get_feed_inbox(conn, param),
})
- .await
- .map(|builder| builder.build().unwrap().to_string())
- .map(|rss| {
+ .await?
+ .map_err(ErrorBadRequest)?;
+
+ let rss = builder.build().map_err(ErrorBadRequest)?.to_string();
+
+ Ok(
HttpResponse::Ok()
.content_type("application/rss+xml")
- .body(rss)
- })
- .map_err(ErrorBadRequest)?;
- Ok(res)
+ .body(rss),
+ )
}
fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
conn: &PgConnection,
sort_type: &SortType,
user_name: String,
-) -> Result<ChannelBuilder, failure::Error> {
+) -> Result<ChannelBuilder, LemmyError> {
let site_view = SiteView::read(&conn)?;
let user = User_::find_by_username(&conn, &user_name)?;
let user_url = user.get_profile_url();
conn: &PgConnection,
sort_type: &SortType,
community_name: String,
-) -> Result<ChannelBuilder, failure::Error> {
+) -> Result<ChannelBuilder, LemmyError> {
let site_view = SiteView::read(&conn)?;
let community = Community::read_from_name(&conn, &community_name)?;
conn: &PgConnection,
sort_type: &SortType,
jwt: String,
-) -> Result<ChannelBuilder, failure::Error> {
+) -> Result<ChannelBuilder, LemmyError> {
let site_view = SiteView::read(&conn)?;
let user_id = Claims::decode(&jwt)?.claims.id;
Ok(channel_builder)
}
-fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, failure::Error> {
+fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, LemmyError> {
let site_view = SiteView::read(&conn)?;
let user_id = Claims::decode(&jwt)?.claims.id;
use crate::{
apub::get_apub_protocol_string,
+ blocking,
db::site_view::SiteView,
routes::DbPoolParam,
version,
+ LemmyError,
Settings,
};
use actix_web::{body::Body, error::ErrorBadRequest, *};
.route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
}
-async fn node_info_well_known() -> Result<HttpResponse<Body>, failure::Error> {
+async fn node_info_well_known() -> Result<HttpResponse<Body>, LemmyError> {
let node_info = NodeInfoWellKnown {
links: NodeInfoWellKnownLinks {
rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?,
}
async fn node_info(db: DbPoolParam) -> Result<HttpResponse, Error> {
- let res = web::block(move || {
- let conn = db.get()?;
- let site_view = match SiteView::read(&conn) {
- Ok(site_view) => site_view,
- Err(_) => return Err(format_err!("not_found")),
- };
- let protocols = if Settings::get().federation.enabled {
- vec!["activitypub".to_string()]
- } else {
- vec![]
- };
- Ok(NodeInfo {
- version: "2.0".to_string(),
- software: NodeInfoSoftware {
- name: "lemmy".to_string(),
- version: version::VERSION.to_string(),
- },
- protocols,
- usage: NodeInfoUsage {
- users: NodeInfoUsers {
- total: site_view.number_of_users,
- },
- local_posts: site_view.number_of_posts,
- local_comments: site_view.number_of_comments,
- open_registrations: site_view.open_registration,
+ let site_view = blocking(&db, SiteView::read)
+ .await?
+ .map_err(|_| ErrorBadRequest(LemmyError::from(format_err!("not_found"))))?;
+
+ let protocols = if Settings::get().federation.enabled {
+ vec!["activitypub".to_string()]
+ } else {
+ vec![]
+ };
+
+ let json = NodeInfo {
+ version: "2.0".to_string(),
+ software: NodeInfoSoftware {
+ name: "lemmy".to_string(),
+ version: version::VERSION.to_string(),
+ },
+ protocols,
+ usage: NodeInfoUsage {
+ users: NodeInfoUsers {
+ total: site_view.number_of_users,
},
- })
- })
- .await
- .map(|json| HttpResponse::Ok().json(json))
- .map_err(ErrorBadRequest)?;
- Ok(res)
+ local_posts: site_view.number_of_posts,
+ local_comments: site_view.number_of_comments,
+ open_registrations: site_view.open_registration,
+ },
+ };
+
+ Ok(HttpResponse::Ok().json(json))
}
#[derive(Serialize, Deserialize, Debug)]
use crate::{
+ blocking,
db::{community::Community, user::User_},
routes::DbPoolParam,
+ LemmyError,
Settings,
};
use actix_web::{error::ErrorBadRequest, web::Query, *};
info: Query<Params>,
db: DbPoolParam,
) -> Result<HttpResponse, Error> {
- let res = web::block(move || {
- let conn = db.get()?;
+ let community_regex_parsed = WEBFINGER_COMMUNITY_REGEX
+ .captures(&info.resource)
+ .map(|c| c.get(1))
+ .flatten();
- let community_regex_parsed = WEBFINGER_COMMUNITY_REGEX
- .captures(&info.resource)
- .map(|c| c.get(1))
- .flatten();
+ let user_regex_parsed = WEBFINGER_USER_REGEX
+ .captures(&info.resource)
+ .map(|c| c.get(1))
+ .flatten();
- let user_regex_parsed = WEBFINGER_USER_REGEX
- .captures(&info.resource)
- .map(|c| c.get(1))
- .flatten();
+ let url = if let Some(community_name) = community_regex_parsed {
+ let community_name = community_name.as_str().to_owned();
+ // Make sure the requested community exists.
+ blocking(&db, move |conn| {
+ Community::read_from_name(conn, &community_name)
+ })
+ .await?
+ .map_err(|_| ErrorBadRequest(LemmyError::from(format_err!("not_found"))))?
+ .actor_id
+ } else if let Some(user_name) = user_regex_parsed {
+ let user_name = user_name.as_str().to_owned();
+ // Make sure the requested user exists.
+ blocking(&db, move |conn| User_::read_from_name(conn, &user_name))
+ .await?
+ .map_err(|_| ErrorBadRequest(LemmyError::from(format_err!("not_found"))))?
+ .actor_id
+ } else {
+ return Err(ErrorBadRequest(LemmyError::from(format_err!("not_found"))));
+ };
- let url = if let Some(community_name) = community_regex_parsed {
- // Make sure the requested community exists.
- let community = match Community::read_from_name(&conn, &community_name.as_str()) {
- Ok(o) => o,
- Err(_) => return Err(format_err!("not_found")),
- };
- community.actor_id
- } else if let Some(user_name) = user_regex_parsed {
- // Make sure the requested user exists.
- let user = match User_::read_from_name(&conn, &user_name.as_str()) {
- Ok(o) => o,
- Err(_) => return Err(format_err!("not_found")),
- };
- user.actor_id
- } else {
- return Err(format_err!("not_found"));
- };
+ let json = WebFingerResponse {
+ subject: info.resource.to_owned(),
+ aliases: vec![url.to_owned()],
+ links: vec![
+ WebFingerLink {
+ rel: Some("http://webfinger.net/rel/profile-page".to_string()),
+ type_: Some("text/html".to_string()),
+ href: Some(url.to_owned()),
+ template: None,
+ },
+ WebFingerLink {
+ rel: Some("self".to_string()),
+ type_: Some("application/activity+json".to_string()),
+ href: Some(url),
+ template: None,
+ }, // TODO: this also needs to return the subscribe link once that's implemented
+ //{
+ // "rel": "http://ostatus.org/schema/1.0/subscribe",
+ // "template": "https://my_instance.com/authorize_interaction?uri={uri}"
+ //}
+ ],
+ };
- let wf_res = WebFingerResponse {
- subject: info.resource.to_owned(),
- aliases: vec![url.to_owned()],
- links: vec![
- WebFingerLink {
- rel: Some("http://webfinger.net/rel/profile-page".to_string()),
- type_: Some("text/html".to_string()),
- href: Some(url.to_owned()),
- template: None,
- },
- WebFingerLink {
- rel: Some("self".to_string()),
- type_: Some("application/activity+json".to_string()),
- href: Some(url),
- template: None,
- }, // TODO: this also needs to return the subscribe link once that's implemented
- //{
- // "rel": "http://ostatus.org/schema/1.0/subscribe",
- // "template": "https://my_instance.com/authorize_interaction?uri={uri}"
- //}
- ],
- };
-
- Ok(wf_res)
- })
- .await
- .map(|json| HttpResponse::Ok().json(json))
- .map_err(ErrorBadRequest)?;
- Ok(res)
+ Ok(HttpResponse::Ok().json(json))
}
+use crate::LemmyError;
use config::{Config, ConfigError, Environment, File};
-use failure::Error;
use serde::Deserialize;
use std::{env, fs, net::IpAddr, sync::RwLock};
format!("{}/api/v1", self.hostname)
}
- pub fn read_config_file() -> Result<String, Error> {
+ pub fn read_config_file() -> Result<String, LemmyError> {
Ok(fs::read_to_string(CONFIG_FILE)?)
}
- pub fn save_config_file(data: &str) -> Result<String, Error> {
+ pub fn save_config_file(data: &str) -> Result<String, LemmyError> {
fs::write(CONFIG_FILE, data)?;
// Reload the new settings
r2d2::{ConnectionManager, Pool},
PgConnection,
};
-use failure::Error;
use log::{error, info};
use rand::{rngs::ThreadRng, Rng};
use serde::{Deserialize, Serialize};
websocket::UserOperation,
CommunityId,
ConnectionId,
+ DbPool,
IPAddr,
+ LemmyError,
PostId,
UserId,
};
+use actix_web::client::Client;
/// Chat server sends this messages to session
#[derive(Message)]
/// Rate limiting based on rate type and IP addr
rate_limiter: RateLimit,
+
+ /// An HTTP Client
+ client: Client,
}
impl ChatServer {
pub fn startup(
pool: Pool<ConnectionManager<PgConnection>>,
rate_limiter: RateLimit,
+ client: Client,
) -> ChatServer {
ChatServer {
sessions: HashMap::new(),
rng: rand::thread_rng(),
pool,
rate_limiter,
+ client,
}
}
response: &Response,
post_id: PostId,
my_id: Option<ConnectionId>,
- ) -> Result<(), Error>
+ ) -> Result<(), LemmyError>
where
Response: Serialize,
{
response: &Response,
community_id: CommunityId,
my_id: Option<ConnectionId>,
- ) -> Result<(), Error>
+ ) -> Result<(), LemmyError>
where
Response: Serialize,
{
op: &UserOperation,
response: &Response,
my_id: Option<ConnectionId>,
- ) -> Result<(), Error>
+ ) -> Result<(), LemmyError>
where
Response: Serialize,
{
response: &Response,
recipient_id: UserId,
my_id: Option<ConnectionId>,
- ) -> Result<(), Error>
+ ) -> Result<(), LemmyError>
where
Response: Serialize,
{
user_operation: &UserOperation,
comment: &CommentResponse,
my_id: Option<ConnectionId>,
- ) -> Result<(), Error> {
+ ) -> Result<(), LemmyError> {
let mut comment_reply_sent = comment.clone();
comment_reply_sent.comment.my_vote = None;
comment_reply_sent.comment.user_id = None;
user_operation: &UserOperation,
post: &PostResponse,
my_id: Option<ConnectionId>,
- ) -> Result<(), Error> {
+ ) -> Result<(), LemmyError> {
let community_id = post.post.community_id;
// Don't send my data with it
&mut self,
msg: StandardMessage,
ctx: &mut Context<Self>,
- ) -> impl Future<Output = Result<String, Error>> {
+ ) -> impl Future<Output = Result<String, LemmyError>> {
let addr = ctx.address();
let pool = self.pool.clone();
let rate_limiter = self.rate_limiter.clone();
None => "blank_ip".to_string(),
};
+ let client = self.client.clone();
async move {
let msg = msg;
let json: Value = serde_json::from_str(&msg.msg)?;
let user_operation: UserOperation = UserOperation::from_str(&op)?;
+ let args = Args {
+ client,
+ pool,
+ rate_limiter,
+ chatserver: addr,
+ id: msg.id,
+ ip,
+ op: user_operation.clone(),
+ data,
+ };
+
match user_operation {
// User ops
- UserOperation::Login => {
- do_user_operation::<Login>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::Register => {
- do_user_operation::<Register>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::GetUserDetails => {
- do_user_operation::<GetUserDetails>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::GetReplies => {
- do_user_operation::<GetReplies>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::AddAdmin => {
- do_user_operation::<AddAdmin>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::BanUser => {
- do_user_operation::<BanUser>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::GetUserMentions => {
- do_user_operation::<GetUserMentions>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::EditUserMention => {
- do_user_operation::<EditUserMention>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::MarkAllAsRead => {
- do_user_operation::<MarkAllAsRead>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::DeleteAccount => {
- do_user_operation::<DeleteAccount>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::PasswordReset => {
- do_user_operation::<PasswordReset>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::PasswordChange => {
- do_user_operation::<PasswordChange>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
+ UserOperation::Login => do_user_operation::<Login>(args).await,
+ UserOperation::Register => do_user_operation::<Register>(args).await,
+ UserOperation::GetUserDetails => do_user_operation::<GetUserDetails>(args).await,
+ UserOperation::GetReplies => do_user_operation::<GetReplies>(args).await,
+ UserOperation::AddAdmin => do_user_operation::<AddAdmin>(args).await,
+ UserOperation::BanUser => do_user_operation::<BanUser>(args).await,
+ UserOperation::GetUserMentions => do_user_operation::<GetUserMentions>(args).await,
+ UserOperation::EditUserMention => do_user_operation::<EditUserMention>(args).await,
+ UserOperation::MarkAllAsRead => do_user_operation::<MarkAllAsRead>(args).await,
+ UserOperation::DeleteAccount => do_user_operation::<DeleteAccount>(args).await,
+ UserOperation::PasswordReset => do_user_operation::<PasswordReset>(args).await,
+ UserOperation::PasswordChange => do_user_operation::<PasswordChange>(args).await,
UserOperation::CreatePrivateMessage => {
- do_user_operation::<CreatePrivateMessage>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::EditPrivateMessage => {
- do_user_operation::<EditPrivateMessage>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::GetPrivateMessages => {
- do_user_operation::<GetPrivateMessages>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::UserJoin => {
- do_user_operation::<UserJoin>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::SaveUserSettings => {
- do_user_operation::<SaveUserSettings>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
+ do_user_operation::<CreatePrivateMessage>(args).await
}
+ UserOperation::EditPrivateMessage => do_user_operation::<EditPrivateMessage>(args).await,
+ UserOperation::GetPrivateMessages => do_user_operation::<GetPrivateMessages>(args).await,
+ UserOperation::UserJoin => do_user_operation::<UserJoin>(args).await,
+ UserOperation::SaveUserSettings => do_user_operation::<SaveUserSettings>(args).await,
// Site ops
- UserOperation::GetModlog => {
- do_user_operation::<GetModlog>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::CreateSite => {
- do_user_operation::<CreateSite>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::EditSite => {
- do_user_operation::<EditSite>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::GetSite => {
- do_user_operation::<GetSite>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::GetSiteConfig => {
- do_user_operation::<GetSiteConfig>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::SaveSiteConfig => {
- do_user_operation::<SaveSiteConfig>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::Search => {
- do_user_operation::<Search>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::TransferCommunity => {
- do_user_operation::<TransferCommunity>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::TransferSite => {
- do_user_operation::<TransferSite>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::ListCategories => {
- do_user_operation::<ListCategories>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
+ UserOperation::GetModlog => do_user_operation::<GetModlog>(args).await,
+ UserOperation::CreateSite => do_user_operation::<CreateSite>(args).await,
+ UserOperation::EditSite => do_user_operation::<EditSite>(args).await,
+ UserOperation::GetSite => do_user_operation::<GetSite>(args).await,
+ UserOperation::GetSiteConfig => do_user_operation::<GetSiteConfig>(args).await,
+ UserOperation::SaveSiteConfig => do_user_operation::<SaveSiteConfig>(args).await,
+ UserOperation::Search => do_user_operation::<Search>(args).await,
+ UserOperation::TransferCommunity => do_user_operation::<TransferCommunity>(args).await,
+ UserOperation::TransferSite => do_user_operation::<TransferSite>(args).await,
+ UserOperation::ListCategories => do_user_operation::<ListCategories>(args).await,
// Community ops
- UserOperation::GetCommunity => {
- do_user_operation::<GetCommunity>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::ListCommunities => {
- do_user_operation::<ListCommunities>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::CreateCommunity => {
- do_user_operation::<CreateCommunity>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::EditCommunity => {
- do_user_operation::<EditCommunity>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::FollowCommunity => {
- do_user_operation::<FollowCommunity>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
+ UserOperation::GetCommunity => do_user_operation::<GetCommunity>(args).await,
+ UserOperation::ListCommunities => do_user_operation::<ListCommunities>(args).await,
+ UserOperation::CreateCommunity => do_user_operation::<CreateCommunity>(args).await,
+ UserOperation::EditCommunity => do_user_operation::<EditCommunity>(args).await,
+ UserOperation::FollowCommunity => do_user_operation::<FollowCommunity>(args).await,
UserOperation::GetFollowedCommunities => {
- do_user_operation::<GetFollowedCommunities>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::BanFromCommunity => {
- do_user_operation::<BanFromCommunity>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::AddModToCommunity => {
- do_user_operation::<AddModToCommunity>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
+ do_user_operation::<GetFollowedCommunities>(args).await
}
+ UserOperation::BanFromCommunity => do_user_operation::<BanFromCommunity>(args).await,
+ UserOperation::AddModToCommunity => do_user_operation::<AddModToCommunity>(args).await,
// Post ops
- UserOperation::CreatePost => {
- do_user_operation::<CreatePost>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::GetPost => {
- do_user_operation::<GetPost>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::GetPosts => {
- do_user_operation::<GetPosts>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::EditPost => {
- do_user_operation::<EditPost>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
- UserOperation::CreatePostLike => {
- do_user_operation::<CreatePostLike>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::SavePost => {
- do_user_operation::<SavePost>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
- .await
- }
+ UserOperation::CreatePost => do_user_operation::<CreatePost>(args).await,
+ UserOperation::GetPost => do_user_operation::<GetPost>(args).await,
+ UserOperation::GetPosts => do_user_operation::<GetPosts>(args).await,
+ UserOperation::EditPost => do_user_operation::<EditPost>(args).await,
+ UserOperation::CreatePostLike => do_user_operation::<CreatePostLike>(args).await,
+ UserOperation::SavePost => do_user_operation::<SavePost>(args).await,
// Comment ops
- UserOperation::CreateComment => {
- do_user_operation::<CreateComment>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::EditComment => {
- do_user_operation::<EditComment>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::SaveComment => {
- do_user_operation::<SaveComment>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::GetComments => {
- do_user_operation::<GetComments>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
- UserOperation::CreateCommentLike => {
- do_user_operation::<CreateCommentLike>(
- pool,
- rate_limiter,
- addr,
- msg.id,
- ip,
- user_operation,
- data,
- )
- .await
- }
+ UserOperation::CreateComment => do_user_operation::<CreateComment>(args).await,
+ UserOperation::EditComment => do_user_operation::<EditComment>(args).await,
+ UserOperation::SaveComment => do_user_operation::<SaveComment>(args).await,
+ UserOperation::GetComments => do_user_operation::<GetComments>(args).await,
+ UserOperation::CreateCommentLike => do_user_operation::<CreateCommentLike>(args).await,
}
}
}
}
-async fn do_user_operation<'a, Data>(
- pool: Pool<ConnectionManager<PgConnection>>,
+struct Args<'a> {
+ client: Client,
+ pool: DbPool,
rate_limiter: RateLimit,
chatserver: Addr<ChatServer>,
id: ConnectionId,
ip: IPAddr,
op: UserOperation,
- data: &str,
-) -> Result<String, Error>
+ data: &'a str,
+}
+
+async fn do_user_operation<'a, 'b, Data>(args: Args<'b>) -> Result<String, LemmyError>
where
for<'de> Data: Deserialize<'de> + 'a,
Oper<Data>: Perform,
{
+ let Args {
+ client,
+ pool,
+ rate_limiter,
+ chatserver,
+ id,
+ ip,
+ op,
+ data,
+ } = args;
+
let ws_info = WebsocketInfo {
chatserver,
id: Some(id),
let data = data.to_string();
let op2 = op.clone();
+ let client = client.clone();
let fut = async move {
- actix_web::web::block(move || {
- let parsed_data: Data = serde_json::from_str(&data)?;
- let res = Oper::new(parsed_data).perform(pool, Some(ws_info))?;
- to_json_string(&op, &res)
- })
- .await
- .map_err(|e| match e {
- actix_web::error::BlockingError::Error(e) => e,
- _ => APIError::err("Operation canceled").into(),
- })
+ let pool = pool.clone();
+ let parsed_data: Data = serde_json::from_str(&data)?;
+ let res = Oper::new(parsed_data, client)
+ .perform(&pool, Some(ws_info))
+ .await?;
+ to_json_string(&op, &res)
};
match op2 {
data: T,
}
-fn to_json_string<Response>(op: &UserOperation, data: &Response) -> Result<String, Error>
+fn to_json_string<Response>(op: &UserOperation, data: &Response) -> Result<String, LemmyError>
where
Response: Serialize,
{
});
describe('follow_accept', () => {
- test('/u/lemmy_alpha follows and accepts lemmy_beta/c/main', async () => {
- // Make sure lemmy_beta/c/main is cached on lemmy_alpha
+ test('/u/lemmy_alpha follows and accepts lemmy-beta/c/main', async () => {
+ // Make sure lemmy-beta/c/main is cached on lemmy_alpha
// Use short-hand search url
- let searchUrl = `${lemmyAlphaApiUrl}/search?q=!main@lemmy_beta:8550&type_=All&sort=TopAll`;
+ let searchUrl = `${lemmyAlphaApiUrl}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`;
let searchResponse: SearchResponse = await fetch(searchUrl, {
method: 'GET',
// Also make G follow B
// Use short-hand search url
- let searchUrlG = `${lemmyGammaApiUrl}/search?q=!main@lemmy_beta:8550&type_=All&sort=TopAll`;
+ let searchUrlG = `${lemmyGammaApiUrl}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`;
let searchResponseG: SearchResponse = await fetch(searchUrlG, {
method: 'GET',
// Lemmy alpha responds to their own comment, but mentions lemmy beta.
// Make sure lemmy beta gets that in their inbox.
- let mentionContent = 'A test mention of @lemmy_beta@lemmy_beta:8550';
+ let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8550';
let mentionCommentForm: CommentForm = {
content: mentionContent,
post_id: 2,
expect(createCommunityRes.community.name).toBe(communityName);
// Cache it on lemmy_alpha
- let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy_beta:8550/c/${communityName}&type_=All&sort=TopAll`;
+ let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy-beta:8550/c/${communityName}&type_=All&sort=TopAll`;
let searchResponse: SearchResponse = await fetch(searchUrl, {
method: 'GET',
}).then(d => d.json());
expect(createCommunityRes.community.name).toBe(communityName);
// Cache it on lemmy_alpha
- let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy_beta:8550/c/${communityName}&type_=All&sort=TopAll`;
+ let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy-beta:8550/c/${communityName}&type_=All&sort=TopAll`;
let searchResponse: SearchResponse = await fetch(searchUrl, {
method: 'GET',
}).then(d => d.json());
// Create a test comment on Gamma, make sure it gets announced to alpha
let commentContent =
- 'A jest test federated comment announce, lets mention @lemmy_beta@lemmy_beta:8550';
+ 'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8550';
let commentForm: CommentForm = {
content: commentContent,
expect(createChildCommentRes.comment.content).toBe(childCommentContent);
// Follow again, for other tests
- let searchUrl = `${lemmyAlphaApiUrl}/search?q=!main@lemmy_beta:8550&type_=All&sort=TopAll`;
+ let searchUrl = `${lemmyAlphaApiUrl}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`;
let searchResponse: SearchResponse = await fetch(searchUrl, {
method: 'GET',