mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Merge remote-tracking branch 'yerba/main' into main
This commit is contained in:
commit
4de80dc29d
142
Cargo.lock
generated
142
Cargo.lock
generated
@ -1113,12 +1113,6 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "dotenv"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.6"
|
||||
@ -1809,6 +1803,92 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"awc",
|
||||
"background-jobs",
|
||||
"base64 0.12.3",
|
||||
"bcrypt",
|
||||
"captcha",
|
||||
"chrono",
|
||||
"diesel",
|
||||
"futures",
|
||||
"http",
|
||||
"http-signature-normalization-actix",
|
||||
"itertools",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
"lemmy_apub",
|
||||
"lemmy_db",
|
||||
"lemmy_rate_limit",
|
||||
"lemmy_structs",
|
||||
"lemmy_utils",
|
||||
"lemmy_websocket",
|
||||
"log",
|
||||
"openssl",
|
||||
"percent-encoding",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"serde 1.0.116",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"url",
|
||||
"uuid 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_apub"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"activitystreams",
|
||||
"activitystreams-ext",
|
||||
"actix",
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"awc",
|
||||
"background-jobs",
|
||||
"base64 0.12.3",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"diesel",
|
||||
"futures",
|
||||
"http",
|
||||
"http-signature-normalization-actix",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"lemmy_db",
|
||||
"lemmy_structs",
|
||||
"lemmy_utils",
|
||||
"lemmy_websocket",
|
||||
"log",
|
||||
"openssl",
|
||||
"percent-encoding",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"serde 1.0.116",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"url",
|
||||
"uuid 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_db"
|
||||
version = "0.1.0"
|
||||
@ -1844,58 +1924,41 @@ dependencies = [
|
||||
name = "lemmy_server"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"activitystreams",
|
||||
"activitystreams-ext",
|
||||
"actix",
|
||||
"actix-files",
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
"actix-web-actors",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"awc",
|
||||
"background-jobs",
|
||||
"base64 0.12.3",
|
||||
"bcrypt",
|
||||
"captcha",
|
||||
"cargo-husky",
|
||||
"chrono",
|
||||
"diesel",
|
||||
"diesel_migrations",
|
||||
"dotenv",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"http",
|
||||
"http-signature-normalization-actix",
|
||||
"itertools",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
"lemmy_api",
|
||||
"lemmy_apub",
|
||||
"lemmy_db",
|
||||
"lemmy_rate_limit",
|
||||
"lemmy_structs",
|
||||
"lemmy_utils",
|
||||
"lemmy_websocket",
|
||||
"log",
|
||||
"openssl",
|
||||
"percent-encoding",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"rss",
|
||||
"serde 1.0.116",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"url",
|
||||
"uuid 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_structs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-web",
|
||||
"chrono",
|
||||
"diesel",
|
||||
@ -1903,8 +1966,7 @@ dependencies = [
|
||||
"lemmy_utils",
|
||||
"log",
|
||||
"serde 1.0.116",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1924,12 +1986,36 @@ dependencies = [
|
||||
"openssl",
|
||||
"rand 0.7.3",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"serde 1.0.116",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_websocket"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"anyhow",
|
||||
"background-jobs",
|
||||
"chrono",
|
||||
"diesel",
|
||||
"lemmy_db",
|
||||
"lemmy_rate_limit",
|
||||
"lemmy_structs",
|
||||
"lemmy_utils",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"serde 1.0.116",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.9.3"
|
||||
|
25
Cargo.toml
25
Cargo.toml
@ -8,56 +8,43 @@ lto = true
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"lemmy_api",
|
||||
"lemmy_apub",
|
||||
"lemmy_utils",
|
||||
"lemmy_db",
|
||||
"lemmy_structs",
|
||||
"lemmy_rate_limit",
|
||||
"lemmy_websocket",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
lemmy_api = { path = "./lemmy_api" }
|
||||
lemmy_apub = { path = "./lemmy_apub" }
|
||||
lemmy_utils = { path = "./lemmy_utils" }
|
||||
lemmy_db = { path = "./lemmy_db" }
|
||||
lemmy_structs = { path = "./lemmy_structs" }
|
||||
lemmy_rate_limit = { path = "./lemmy_rate_limit" }
|
||||
lemmy_websocket = { path = "./lemmy_websocket" }
|
||||
diesel = "1.4"
|
||||
diesel_migrations = "1.4"
|
||||
dotenv = "0.15"
|
||||
activitystreams = "0.7.0-alpha.4"
|
||||
activitystreams-ext = "0.1.0-alpha.2"
|
||||
bcrypt = "0.8"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"]}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
actix = "0.10"
|
||||
actix-web = { version = "3.0", default-features = false, features = ["rustls"] }
|
||||
actix-files = { version = "0.3", default-features = false }
|
||||
actix-web-actors = { version = "3.0", default-features = false }
|
||||
actix-rt = { version = "1.1", default-features = false }
|
||||
awc = { version = "2.0", default-features = false }
|
||||
log = "0.4"
|
||||
env_logger = "0.7"
|
||||
rand = "0.7"
|
||||
strum = "0.19"
|
||||
strum_macros = "0.19"
|
||||
jsonwebtoken = "7.0"
|
||||
lazy_static = "1.3"
|
||||
rss = "1.9"
|
||||
url = { version = "2.1", features = ["serde"] }
|
||||
percent-encoding = "2.1"
|
||||
openssl = "0.10"
|
||||
http = "0.2"
|
||||
http-signature-normalization-actix = { version = "0.4", default-features = false, features = ["sha-2"] }
|
||||
base64 = "0.12"
|
||||
tokio = "0.2"
|
||||
futures = "0.3"
|
||||
itertools = "0.9"
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
sha2 = "0.9"
|
||||
async-trait = "0.1"
|
||||
captcha = "0.0"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
background-jobs = " 0.8"
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
|
||||
[dev-dependencies.cargo-husky]
|
||||
|
@ -12,6 +12,9 @@ RUN mkdir -p lemmy_db/src/ \
|
||||
lemmy_utils/src/ \
|
||||
lemmy_structs/src/ \
|
||||
lemmy_rate_limit/src/ \
|
||||
lemmy_api/src/ \
|
||||
lemmy_apub/src/ \
|
||||
lemmy_websocket/src/ \
|
||||
lemmy
|
||||
|
||||
# Copy the cargo tomls
|
||||
@ -20,6 +23,9 @@ COPY lemmy_db/Cargo.toml ./lemmy_db/
|
||||
COPY lemmy_utils/Cargo.toml ./lemmy_utils/
|
||||
COPY lemmy_structs/Cargo.toml ./lemmy_structs/
|
||||
COPY lemmy_rate_limit/Cargo.toml ./lemmy_rate_limit/
|
||||
COPY lemmy_api/Cargo.toml ./lemmy_api/
|
||||
COPY lemmy_apub/Cargo.toml ./lemmy_apub/
|
||||
COPY lemmy_websocket/Cargo.toml ./lemmy_websocket/
|
||||
|
||||
# Cache the deps
|
||||
RUN cargo build-deps
|
||||
@ -30,6 +36,9 @@ COPY lemmy_db/src ./lemmy_db/src/
|
||||
COPY lemmy_utils/src/ ./lemmy_utils/src/
|
||||
COPY lemmy_structs/src/ ./lemmy_structs/src/
|
||||
COPY lemmy_rate_limit/src/ ./lemmy_rate_limit/src/
|
||||
COPY lemmy_api/src/ ./lemmy_api/src/
|
||||
COPY lemmy_apub/src/ ./lemmy_apub/src/
|
||||
COPY lemmy_websocket/src/ ./lemmy_websocket/src/
|
||||
COPY migrations ./migrations/
|
||||
|
||||
# Build for debug
|
||||
|
@ -15,11 +15,11 @@ services:
|
||||
depends_on:
|
||||
- pictrs
|
||||
- iframely
|
||||
- lemmy-alpha
|
||||
- lemmy-beta
|
||||
- lemmy-gamma
|
||||
- lemmy-delta
|
||||
- lemmy-epsilon
|
||||
- lemmy-alpha-ui
|
||||
- lemmy-beta-ui
|
||||
- lemmy-gamma-ui
|
||||
- lemmy-delta-ui
|
||||
- lemmy-epsilon-ui
|
||||
|
||||
pictrs:
|
||||
restart: always
|
||||
|
@ -3,6 +3,12 @@ events {
|
||||
}
|
||||
|
||||
http {
|
||||
upstream lemmy-alpha {
|
||||
server "lemmy-alpha:8541";
|
||||
}
|
||||
upstream lemmy-alpha-ui {
|
||||
server "lemmy-alpha-ui:1234";
|
||||
}
|
||||
server {
|
||||
listen 8540;
|
||||
server_name 127.0.0.1;
|
||||
@ -11,14 +17,19 @@ http {
|
||||
# Upload limit for pictshare
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v1 {
|
||||
proxy_pass http://lemmy-alpha:8541/api/v1;
|
||||
location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
|
||||
proxy_pass http://lemmy-alpha;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://lemmy-alpha-ui:1234;
|
||||
set $proxpass http://lemmy-alpha-ui;
|
||||
if ($http_accept = "application/activity+json") {
|
||||
set $proxpass http://lemmy-alpha;
|
||||
}
|
||||
proxy_pass $proxpass;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
@ -34,6 +45,12 @@ http {
|
||||
}
|
||||
}
|
||||
|
||||
upstream lemmy-beta {
|
||||
server "lemmy-beta:8551";
|
||||
}
|
||||
upstream lemmy-beta-ui {
|
||||
server "lemmy-beta-ui:1234";
|
||||
}
|
||||
server {
|
||||
listen 8550;
|
||||
server_name 127.0.0.1;
|
||||
@ -42,14 +59,19 @@ http {
|
||||
# Upload limit for pictshare
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v1 {
|
||||
proxy_pass http://lemmy-beta:8551/api/v1;
|
||||
location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
|
||||
proxy_pass http://lemmy-beta;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://lemmy-beta-ui:1234;
|
||||
set $proxpass http://lemmy-beta-ui;
|
||||
if ($http_accept = "application/activity+json") {
|
||||
set $proxpass http://lemmy-beta;
|
||||
}
|
||||
proxy_pass $proxpass;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
@ -65,6 +87,12 @@ http {
|
||||
}
|
||||
}
|
||||
|
||||
upstream lemmy-gamma {
|
||||
server "lemmy-gamma:8561";
|
||||
}
|
||||
upstream lemmy-gamma-ui {
|
||||
server "lemmy-gamma-ui:1234";
|
||||
}
|
||||
server {
|
||||
listen 8560;
|
||||
server_name 127.0.0.1;
|
||||
@ -73,14 +101,19 @@ http {
|
||||
# Upload limit for pictshare
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v1 {
|
||||
proxy_pass http://lemmy-gamma:8561/api/v1;
|
||||
location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
|
||||
proxy_pass http://lemmy-gamma;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://lemmy-gamma-ui:1234;
|
||||
set $proxpass http://lemmy-gamma-ui;
|
||||
if ($http_accept = "application/activity+json") {
|
||||
set $proxpass http://lemmy-gamma;
|
||||
}
|
||||
proxy_pass $proxpass;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
@ -96,6 +129,12 @@ http {
|
||||
}
|
||||
}
|
||||
|
||||
upstream lemmy-delta {
|
||||
server "lemmy-delta:8571";
|
||||
}
|
||||
upstream lemmy-delta-ui {
|
||||
server "lemmy-delta-ui:1234";
|
||||
}
|
||||
server {
|
||||
listen 8570;
|
||||
server_name 127.0.0.1;
|
||||
@ -104,14 +143,19 @@ http {
|
||||
# Upload limit for pictshare
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v1 {
|
||||
proxy_pass http://lemmy-delta:8571/api/v1;
|
||||
location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
|
||||
proxy_pass http://lemmy-delta;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://lemmy-delta-ui:1234;
|
||||
set $proxpass http://lemmy-delta-ui;
|
||||
if ($http_accept = "application/activity+json") {
|
||||
set $proxpass http://lemmy-delta;
|
||||
}
|
||||
proxy_pass $proxpass;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
@ -127,6 +171,12 @@ http {
|
||||
}
|
||||
}
|
||||
|
||||
upstream lemmy-epsilon {
|
||||
server "lemmy-epsilon:8581";
|
||||
}
|
||||
upstream lemmy-epsilon-ui {
|
||||
server "lemmy-epsilon-ui:1234";
|
||||
}
|
||||
server {
|
||||
listen 8580;
|
||||
server_name 127.0.0.1;
|
||||
@ -135,14 +185,19 @@ http {
|
||||
# Upload limit for pictshare
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /api/v1 {
|
||||
proxy_pass http://lemmy-epsilon:8581/api/v1;
|
||||
location ~ ^/(api|docs|pictrs|feeds|nodeinfo|.well-known) {
|
||||
proxy_pass http://lemmy-epsilon;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://lemmy-epsilon-ui:1234;
|
||||
set $proxpass http://lemmy-epsilon-ui;
|
||||
if ($http_accept = "application/activity+json") {
|
||||
set $proxpass http://lemmy-epsilon;
|
||||
}
|
||||
proxy_pass $proxpass;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
@ -15,6 +15,9 @@ COPY lemmy_db ./lemmy_db
|
||||
COPY lemmy_utils ./lemmy_utils
|
||||
COPY lemmy_structs ./lemmy_structs
|
||||
COPY lemmy_rate_limit ./lemmy_rate_limit
|
||||
COPY lemmy_api ./lemmy_api
|
||||
COPY lemmy_apub ./lemmy_apub
|
||||
COPY lemmy_websocket ./lemmy_websocket
|
||||
RUN mkdir -p ./src/bin \
|
||||
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
|
||||
RUN cargo build --release
|
||||
|
@ -9,8 +9,8 @@ third_semver=$(echo $new_tag | cut -d "." -f 3)
|
||||
# Setting the version on the front end
|
||||
cd ../../
|
||||
# Setting the version on the backend
|
||||
echo "pub const VERSION: &str = \"$new_tag\";" > "src/version.rs"
|
||||
git add "src/version.rs"
|
||||
echo "pub const VERSION: &str = \"$new_tag\";" > "lemmy_api/src/version.rs"
|
||||
git add "lemmy_api/src/version.rs"
|
||||
# Setting the version for Ansible
|
||||
echo $new_tag > "ansible/VERSION"
|
||||
git add "ansible/VERSION"
|
||||
|
@ -21,10 +21,12 @@ lemmy-epsilon | lemmy_epsilon | [127.0.0.1:8580](http://127.0.0.1:8580) | uses b
|
||||
|
||||
You can log into each using the instance name, and `lemmy` as the password, IE (`lemmy_alpha`, `lemmy`).
|
||||
|
||||
To start federation between instances, visit one of them and search for a user, community or post, like this:
|
||||
- `!main@lemmy-alpha:8540`
|
||||
- `http://lemmy-beta:8550/post/3`
|
||||
- `@lemmy-gamma@lemmy-gamma:8560`
|
||||
To start federation between instances, visit one of them and search for a user, community or post, like this. Note that
|
||||
the Lemmy backend runs on a different port than the frontend, so you have to increment the port number from
|
||||
the URL bar by one.
|
||||
- `!main@lemmy-alpha:8541`
|
||||
- `http://lemmy-beta:8551/post/3`
|
||||
- `@lemmy-gamma@lemmy-gamma:8561`
|
||||
|
||||
Firefox containers are a good way to test them interacting.
|
||||
|
||||
|
49
lemmy_api/Cargo.toml
Normal file
49
lemmy_api/Cargo.toml
Normal file
@ -0,0 +1,49 @@
|
||||
[package]
|
||||
name = "lemmy_api"
|
||||
version = "0.1.0"
|
||||
authors = ["Felix Ableitner <me@nutomic.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "lemmy_api"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
lemmy_apub = { path = "../lemmy_apub" }
|
||||
lemmy_utils = { path = "../lemmy_utils" }
|
||||
lemmy_db = { path = "../lemmy_db" }
|
||||
lemmy_structs = { path = "../lemmy_structs" }
|
||||
lemmy_rate_limit = { path = "../lemmy_rate_limit" }
|
||||
lemmy_websocket = { path = "../lemmy_websocket" }
|
||||
diesel = "1.4"
|
||||
bcrypt = "0.8"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"]}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
actix = "0.10"
|
||||
actix-web = { version = "3.0", default-features = false }
|
||||
actix-rt = { version = "1.1", default-features = false }
|
||||
awc = { version = "2.0", default-features = false }
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
strum = "0.19"
|
||||
strum_macros = "0.19"
|
||||
jsonwebtoken = "7.0"
|
||||
lazy_static = "1.3"
|
||||
url = { version = "2.1", features = ["serde"] }
|
||||
percent-encoding = "2.1"
|
||||
openssl = "0.10"
|
||||
http = "0.2"
|
||||
http-signature-normalization-actix = { version = "0.4", default-features = false, features = ["sha-2"] }
|
||||
base64 = "0.12"
|
||||
tokio = "0.2"
|
||||
futures = "0.3"
|
||||
itertools = "0.9"
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
sha2 = "0.9"
|
||||
async-trait = "0.1"
|
||||
captcha = "0.0"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
background-jobs = " 0.8"
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
@ -1,16 +1,13 @@
|
||||
use crate::{
|
||||
api::{
|
||||
check_community_ban,
|
||||
get_post,
|
||||
get_user_from_jwt,
|
||||
get_user_from_jwt_opt,
|
||||
is_mod_or_admin,
|
||||
Perform,
|
||||
},
|
||||
apub::{ApubLikeableType, ApubObjectType},
|
||||
LemmyContext,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use lemmy_apub::{ApubLikeableType, ApubObjectType};
|
||||
use lemmy_db::{
|
||||
comment::*,
|
||||
comment_view::*,
|
||||
@ -24,12 +21,7 @@ use lemmy_db::{
|
||||
Saveable,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
comment::*,
|
||||
send_local_notifs,
|
||||
websocket::{SendComment, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, comment::*, send_local_notifs};
|
||||
use lemmy_utils::{
|
||||
apub::{make_apub_endpoint, EndpointType},
|
||||
utils::{remove_slurs, scrape_text_for_mentions},
|
||||
@ -37,6 +29,7 @@ use lemmy_utils::{
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
@ -1,10 +1,7 @@
|
||||
use crate::{
|
||||
api::{get_user_from_jwt, get_user_from_jwt_opt, is_admin, is_mod_or_admin, Perform},
|
||||
apub::ActorType,
|
||||
LemmyContext,
|
||||
};
|
||||
use crate::{get_user_from_jwt, get_user_from_jwt_opt, is_admin, is_mod_or_admin, Perform};
|
||||
use actix_web::web::Data;
|
||||
use anyhow::Context;
|
||||
use lemmy_apub::ActorType;
|
||||
use lemmy_db::{
|
||||
comment::Comment,
|
||||
comment_view::CommentQueryBuilder,
|
||||
@ -22,16 +19,7 @@ use lemmy_db::{
|
||||
Joinable,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
community::*,
|
||||
websocket::{
|
||||
GetCommunityUsersOnline,
|
||||
JoinCommunityRoom,
|
||||
SendCommunityRoomMessage,
|
||||
UserOperation,
|
||||
},
|
||||
};
|
||||
use lemmy_structs::{blocking, community::*};
|
||||
use lemmy_utils::{
|
||||
apub::{generate_actor_keypair, make_apub_endpoint, EndpointType},
|
||||
location_info,
|
||||
@ -40,6 +28,11 @@ use lemmy_utils::{
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{
|
||||
messages::{GetCommunityUsersOnline, JoinCommunityRoom, SendCommunityRoomMessage},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
539
lemmy_api/src/lib.rs
Normal file
539
lemmy_api/src/lib.rs
Normal file
@ -0,0 +1,539 @@
|
||||
use crate::claims::Claims;
|
||||
use actix_web::{web, web::Data};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_db::{
|
||||
community::Community,
|
||||
community_view::CommunityUserBanView,
|
||||
post::Post,
|
||||
user::User_,
|
||||
Crud,
|
||||
DbPool,
|
||||
};
|
||||
use lemmy_structs::{blocking, comment::*, community::*, post::*, site::*, user::*};
|
||||
use lemmy_utils::{
|
||||
apub::get_apub_protocol_string,
|
||||
request::{retry, RecvError},
|
||||
settings::Settings,
|
||||
APIError,
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
|
||||
use log::error;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use std::process::Command;
|
||||
use url::Url;
|
||||
|
||||
pub mod claims;
|
||||
pub mod comment;
|
||||
pub mod community;
|
||||
pub mod post;
|
||||
pub mod site;
|
||||
pub mod user;
|
||||
pub mod version;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait Perform {
|
||||
type Response: serde::ser::Serialize + Send;
|
||||
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<Self::Response, LemmyError>;
|
||||
}
|
||||
|
||||
pub(in crate) async fn is_mod_or_admin(
|
||||
pool: &DbPool,
|
||||
user_id: i32,
|
||||
community_id: i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let is_mod_or_admin = blocking(pool, move |conn| {
|
||||
Community::is_mod_or_admin(conn, user_id, community_id)
|
||||
})
|
||||
.await?;
|
||||
if !is_mod_or_admin {
|
||||
return Err(APIError::err("not_a_mod_or_admin").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub async fn is_admin(pool: &DbPool, user_id: i32) -> Result<(), LemmyError> {
|
||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
||||
if !user.admin {
|
||||
return Err(APIError::err("not_an_admin").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(in crate) async fn get_post(post_id: i32, pool: &DbPool) -> Result<Post, LemmyError> {
|
||||
match blocking(pool, move |conn| Post::read(conn, post_id)).await? {
|
||||
Ok(post) => Ok(post),
|
||||
Err(_e) => Err(APIError::err("couldnt_find_post").into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate) async fn get_user_from_jwt(jwt: &str, pool: &DbPool) -> Result<User_, LemmyError> {
|
||||
let claims = match Claims::decode(&jwt) {
|
||||
Ok(claims) => claims.claims,
|
||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
};
|
||||
let user_id = claims.id;
|
||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
||||
// Check for a site ban
|
||||
if user.banned {
|
||||
return Err(APIError::err("site_ban").into());
|
||||
}
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub(in crate) async fn get_user_from_jwt_opt(
|
||||
jwt: &Option<String>,
|
||||
pool: &DbPool,
|
||||
) -> Result<Option<User_>, LemmyError> {
|
||||
match jwt {
|
||||
Some(jwt) => Ok(Some(get_user_from_jwt(jwt, pool).await?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate) async fn check_community_ban(
|
||||
user_id: i32,
|
||||
community_id: i32,
|
||||
pool: &DbPool,
|
||||
) -> Result<(), LemmyError> {
|
||||
let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
||||
if blocking(pool, is_banned).await? {
|
||||
Err(APIError::err("community_ban").into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate) async fn linked_instances(pool: &DbPool) -> Result<Vec<String>, LemmyError> {
|
||||
let mut instances: Vec<String> = Vec::new();
|
||||
|
||||
if Settings::get().federation.enabled {
|
||||
let distinct_communities = blocking(pool, move |conn| {
|
||||
Community::distinct_federated_communities(conn)
|
||||
})
|
||||
.await??;
|
||||
|
||||
instances = distinct_communities
|
||||
.iter()
|
||||
.map(|actor_id| Ok(Url::parse(actor_id)?.host_str().unwrap_or("").to_string()))
|
||||
.collect::<Result<Vec<String>, LemmyError>>()?;
|
||||
|
||||
instances.append(&mut Settings::get().get_allowed_instances());
|
||||
instances.retain(|a| {
|
||||
!Settings::get().get_blocked_instances().contains(a)
|
||||
&& !a.eq("")
|
||||
&& !a.eq(&Settings::get().hostname)
|
||||
});
|
||||
|
||||
// Sort and remove dupes
|
||||
instances.sort_unstable();
|
||||
instances.dedup();
|
||||
}
|
||||
|
||||
Ok(instances)
|
||||
}
|
||||
|
||||
pub async fn match_websocket_operation(
|
||||
context: LemmyContext,
|
||||
id: ConnectionId,
|
||||
op: UserOperation,
|
||||
data: &str,
|
||||
) -> Result<String, LemmyError> {
|
||||
match op {
|
||||
// User ops
|
||||
UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
|
||||
UserOperation::Register => do_websocket_operation::<Register>(context, id, op, data).await,
|
||||
UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
|
||||
UserOperation::GetUserDetails => {
|
||||
do_websocket_operation::<GetUserDetails>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
|
||||
UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
|
||||
UserOperation::BanUser => do_websocket_operation::<BanUser>(context, id, op, data).await,
|
||||
UserOperation::GetUserMentions => {
|
||||
do_websocket_operation::<GetUserMentions>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::MarkUserMentionAsRead => {
|
||||
do_websocket_operation::<MarkUserMentionAsRead>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::MarkAllAsRead => {
|
||||
do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::DeleteAccount => {
|
||||
do_websocket_operation::<DeleteAccount>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::PasswordReset => {
|
||||
do_websocket_operation::<PasswordReset>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::PasswordChange => {
|
||||
do_websocket_operation::<PasswordChange>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
|
||||
UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
|
||||
UserOperation::CommunityJoin => {
|
||||
do_websocket_operation::<CommunityJoin>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::SaveUserSettings => {
|
||||
do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
|
||||
}
|
||||
|
||||
// Private Message ops
|
||||
UserOperation::CreatePrivateMessage => {
|
||||
do_websocket_operation::<CreatePrivateMessage>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::EditPrivateMessage => {
|
||||
do_websocket_operation::<EditPrivateMessage>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::DeletePrivateMessage => {
|
||||
do_websocket_operation::<DeletePrivateMessage>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::MarkPrivateMessageAsRead => {
|
||||
do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::GetPrivateMessages => {
|
||||
do_websocket_operation::<GetPrivateMessages>(context, id, op, data).await
|
||||
}
|
||||
|
||||
// Site ops
|
||||
UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
|
||||
UserOperation::CreateSite => do_websocket_operation::<CreateSite>(context, id, op, data).await,
|
||||
UserOperation::EditSite => do_websocket_operation::<EditSite>(context, id, op, data).await,
|
||||
UserOperation::GetSite => do_websocket_operation::<GetSite>(context, id, op, data).await,
|
||||
UserOperation::GetSiteConfig => {
|
||||
do_websocket_operation::<GetSiteConfig>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::SaveSiteConfig => {
|
||||
do_websocket_operation::<SaveSiteConfig>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::Search => do_websocket_operation::<Search>(context, id, op, data).await,
|
||||
UserOperation::TransferCommunity => {
|
||||
do_websocket_operation::<TransferCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::TransferSite => {
|
||||
do_websocket_operation::<TransferSite>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::ListCategories => {
|
||||
do_websocket_operation::<ListCategories>(context, id, op, data).await
|
||||
}
|
||||
|
||||
// Community ops
|
||||
UserOperation::GetCommunity => {
|
||||
do_websocket_operation::<GetCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::ListCommunities => {
|
||||
do_websocket_operation::<ListCommunities>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::CreateCommunity => {
|
||||
do_websocket_operation::<CreateCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::EditCommunity => {
|
||||
do_websocket_operation::<EditCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::DeleteCommunity => {
|
||||
do_websocket_operation::<DeleteCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::RemoveCommunity => {
|
||||
do_websocket_operation::<RemoveCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::FollowCommunity => {
|
||||
do_websocket_operation::<FollowCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::GetFollowedCommunities => {
|
||||
do_websocket_operation::<GetFollowedCommunities>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::BanFromCommunity => {
|
||||
do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::AddModToCommunity => {
|
||||
do_websocket_operation::<AddModToCommunity>(context, id, op, data).await
|
||||
}
|
||||
|
||||
// Post ops
|
||||
UserOperation::CreatePost => do_websocket_operation::<CreatePost>(context, id, op, data).await,
|
||||
UserOperation::GetPost => do_websocket_operation::<GetPost>(context, id, op, data).await,
|
||||
UserOperation::GetPosts => do_websocket_operation::<GetPosts>(context, id, op, data).await,
|
||||
UserOperation::EditPost => do_websocket_operation::<EditPost>(context, id, op, data).await,
|
||||
UserOperation::DeletePost => do_websocket_operation::<DeletePost>(context, id, op, data).await,
|
||||
UserOperation::RemovePost => do_websocket_operation::<RemovePost>(context, id, op, data).await,
|
||||
UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
|
||||
UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
|
||||
UserOperation::CreatePostLike => {
|
||||
do_websocket_operation::<CreatePostLike>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
|
||||
|
||||
// Comment ops
|
||||
UserOperation::CreateComment => {
|
||||
do_websocket_operation::<CreateComment>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::EditComment => {
|
||||
do_websocket_operation::<EditComment>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::DeleteComment => {
|
||||
do_websocket_operation::<DeleteComment>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::RemoveComment => {
|
||||
do_websocket_operation::<RemoveComment>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::MarkCommentAsRead => {
|
||||
do_websocket_operation::<MarkCommentAsRead>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::SaveComment => {
|
||||
do_websocket_operation::<SaveComment>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::GetComments => {
|
||||
do_websocket_operation::<GetComments>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::CreateCommentLike => {
|
||||
do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn do_websocket_operation<'a, 'b, Data>(
|
||||
context: LemmyContext,
|
||||
id: ConnectionId,
|
||||
op: UserOperation,
|
||||
data: &str,
|
||||
) -> Result<String, LemmyError>
|
||||
where
|
||||
for<'de> Data: Deserialize<'de> + 'a,
|
||||
Data: Perform,
|
||||
{
|
||||
let parsed_data: Data = serde_json::from_str(&data)?;
|
||||
let res = parsed_data
|
||||
.perform(&web::Data::new(context), Some(id))
|
||||
.await?;
|
||||
serialize_websocket_message(&op, &res)
|
||||
}
|
||||
|
||||
pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyError> {
|
||||
let mut built_text = String::new();
|
||||
|
||||
// Building proper speech text for espeak
|
||||
for mut c in captcha.chars() {
|
||||
let new_str = if c.is_alphabetic() {
|
||||
if c.is_lowercase() {
|
||||
c.make_ascii_uppercase();
|
||||
format!("lower case {} ... ", c)
|
||||
} else {
|
||||
c.make_ascii_uppercase();
|
||||
format!("capital {} ... ", c)
|
||||
}
|
||||
} else {
|
||||
format!("{} ...", c)
|
||||
};
|
||||
|
||||
built_text.push_str(&new_str);
|
||||
}
|
||||
|
||||
espeak_wav_base64(&built_text)
|
||||
}
|
||||
|
||||
pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
|
||||
// Make a temp file path
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
let file_path = format!("/tmp/lemmy_espeak_{}.wav", &uuid);
|
||||
|
||||
// Write the wav file
|
||||
Command::new("espeak")
|
||||
.arg("-w")
|
||||
.arg(&file_path)
|
||||
.arg(text)
|
||||
.status()?;
|
||||
|
||||
// Read the wav file bytes
|
||||
let bytes = std::fs::read(&file_path)?;
|
||||
|
||||
// Delete the file
|
||||
std::fs::remove_file(file_path)?;
|
||||
|
||||
// Convert to base64
|
||||
let base64 = base64::encode(bytes);
|
||||
|
||||
Ok(base64)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub(crate) struct IframelyResponse {
|
||||
title: Option<String>,
|
||||
description: Option<String>,
|
||||
thumbnail_url: Option<String>,
|
||||
html: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn fetch_iframely(
|
||||
client: &Client,
|
||||
url: &str,
|
||||
) -> Result<IframelyResponse, LemmyError> {
|
||||
let fetch_url = format!("http://iframely/oembed?url={}", url);
|
||||
|
||||
let response = retry(|| client.get(&fetch_url).send()).await?;
|
||||
|
||||
let res: IframelyResponse = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| RecvError(e.to_string()))?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub(crate) struct PictrsResponse {
|
||||
files: Vec<PictrsFile>,
|
||||
msg: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub(crate) struct PictrsFile {
|
||||
file: String,
|
||||
delete_token: String,
|
||||
}
|
||||
|
||||
pub(crate) 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 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(anyhow!("{}", &response.msg).into())
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_iframely_and_pictrs_data(
|
||||
client: &Client,
|
||||
url: Option<String>,
|
||||
) -> (
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
) {
|
||||
match &url {
|
||||
Some(url) => {
|
||||
// Fetch iframely data
|
||||
let (iframely_title, iframely_description, iframely_thumbnail_url, iframely_html) =
|
||||
match fetch_iframely(client, url).await {
|
||||
Ok(res) => (res.title, res.description, res.thumbnail_url, res.html),
|
||||
Err(e) => {
|
||||
error!("iframely err: {}", e);
|
||||
(None, None, None, None)
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch pictrs thumbnail
|
||||
let pictrs_hash = match 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);
|
||||
None
|
||||
}
|
||||
},
|
||||
// Try to generate a small thumbnail if iframely is not supported
|
||||
None => match fetch_pictrs(client, &url).await {
|
||||
Ok(res) => Some(res.files[0].file.to_owned()),
|
||||
Err(e) => {
|
||||
error!("pictrs err: {}", e);
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// The full urls are necessary for federation
|
||||
let pictrs_thumbnail = if let Some(pictrs_hash) = pictrs_hash {
|
||||
Some(format!(
|
||||
"{}://{}/pictrs/image/{}",
|
||||
get_apub_protocol_string(),
|
||||
Settings::get().hostname,
|
||||
pictrs_hash
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(
|
||||
iframely_title,
|
||||
iframely_description,
|
||||
iframely_html,
|
||||
pictrs_thumbnail,
|
||||
)
|
||||
}
|
||||
None => (None, None, None, None),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) 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(|| anyhow!("No Content-Type header"))?
|
||||
.to_str()?
|
||||
.starts_with("image/")
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("Not an image type.").into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{captcha_espeak_wav_base64, is_image_content_type};
|
||||
|
||||
#[test]
|
||||
fn test_image() {
|
||||
actix_rt::System::new("tset_image").block_on(async move {
|
||||
let client = reqwest::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]
|
||||
fn test_espeak() {
|
||||
assert!(captcha_espeak_wav_base64("WxRt2l").is_ok())
|
||||
}
|
||||
|
||||
// These helped with testing
|
||||
// #[test]
|
||||
// fn test_iframely() {
|
||||
// let res = fetch_iframely(client, "https://www.redspark.nu/?p=15341").await;
|
||||
// assert!(res.is_ok());
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_pictshare() {
|
||||
// let res = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpg");
|
||||
// assert!(res.is_ok());
|
||||
// let res_other = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpgaoeu");
|
||||
// assert!(res_other.is_err());
|
||||
// }
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
use crate::{
|
||||
api::{check_community_ban, get_user_from_jwt, get_user_from_jwt_opt, is_mod_or_admin, Perform},
|
||||
apub::{ApubLikeableType, ApubObjectType},
|
||||
check_community_ban,
|
||||
fetch_iframely_and_pictrs_data,
|
||||
LemmyContext,
|
||||
get_user_from_jwt,
|
||||
get_user_from_jwt_opt,
|
||||
is_mod_or_admin,
|
||||
Perform,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use lemmy_apub::{ApubLikeableType, ApubObjectType};
|
||||
use lemmy_db::{
|
||||
comment_view::*,
|
||||
community_view::*,
|
||||
@ -19,11 +22,7 @@ use lemmy_db::{
|
||||
Saveable,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
post::*,
|
||||
websocket::{GetPostUsersOnline, JoinPostRoom, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, post::*};
|
||||
use lemmy_utils::{
|
||||
apub::{make_apub_endpoint, EndpointType},
|
||||
utils::{check_slurs, check_slurs_opt, is_valid_post_title},
|
||||
@ -31,6 +30,11 @@ use lemmy_utils::{
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{
|
||||
messages::{GetPostUsersOnline, JoinPostRoom, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
api::{get_user_from_jwt, get_user_from_jwt_opt, is_admin, linked_instances, Perform},
|
||||
apub::fetcher::search_by_apub_id,
|
||||
get_user_from_jwt,
|
||||
get_user_from_jwt_opt,
|
||||
is_admin,
|
||||
linked_instances,
|
||||
version,
|
||||
LemmyContext,
|
||||
Perform,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use anyhow::Context;
|
||||
use lemmy_apub::fetcher::search_by_apub_id;
|
||||
use lemmy_db::{
|
||||
category::*,
|
||||
comment_view::*,
|
||||
@ -22,12 +25,7 @@ use lemmy_db::{
|
||||
SearchType,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
site::*,
|
||||
user::Register,
|
||||
websocket::{GetUsersOnline, SendAllMessage, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, site::*, user::Register};
|
||||
use lemmy_utils::{
|
||||
location_info,
|
||||
settings::Settings,
|
||||
@ -36,6 +34,11 @@ use lemmy_utils::{
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{
|
||||
messages::{GetUsersOnline, SendAllMessage},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
use log::{debug, info};
|
||||
use std::str::FromStr;
|
||||
|
@ -1,14 +1,17 @@
|
||||
use crate::{
|
||||
api::{claims::Claims, get_user_from_jwt, get_user_from_jwt_opt, is_admin, Perform},
|
||||
apub::ApubObjectType,
|
||||
captcha_espeak_wav_base64,
|
||||
LemmyContext,
|
||||
claims::Claims,
|
||||
get_user_from_jwt,
|
||||
get_user_from_jwt_opt,
|
||||
is_admin,
|
||||
Perform,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use anyhow::Context;
|
||||
use bcrypt::verify;
|
||||
use captcha::{gen, Difficulty};
|
||||
use chrono::Duration;
|
||||
use lemmy_apub::ApubObjectType;
|
||||
use lemmy_db::{
|
||||
comment::*,
|
||||
comment_view::*,
|
||||
@ -34,18 +37,7 @@ use lemmy_db::{
|
||||
ListingType,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
user::*,
|
||||
websocket::{
|
||||
CaptchaItem,
|
||||
CheckCaptcha,
|
||||
JoinUserRoom,
|
||||
SendAllMessage,
|
||||
SendUserRoomMessage,
|
||||
UserOperation,
|
||||
},
|
||||
};
|
||||
use lemmy_structs::{blocking, user::*};
|
||||
use lemmy_utils::{
|
||||
apub::{generate_actor_keypair, make_apub_endpoint, EndpointType},
|
||||
email::send_email,
|
||||
@ -63,6 +55,11 @@ use lemmy_utils::{
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{
|
||||
messages::{CaptchaItem, CheckCaptcha, JoinUserRoom, SendAllMessage, SendUserRoomMessage},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
use log::error;
|
||||
use std::str::FromStr;
|
||||
|
47
lemmy_apub/Cargo.toml
Normal file
47
lemmy_apub/Cargo.toml
Normal file
@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "lemmy_apub"
|
||||
version = "0.1.0"
|
||||
authors = ["Felix Ableitner <me@nutomic.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "lemmy_apub"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { path = "../lemmy_utils" }
|
||||
lemmy_db = { path = "../lemmy_db" }
|
||||
lemmy_structs = { path = "../lemmy_structs" }
|
||||
lemmy_websocket = { path = "../lemmy_websocket" }
|
||||
diesel = "1.4"
|
||||
activitystreams = "0.7.0-alpha.4"
|
||||
activitystreams-ext = "0.1.0-alpha.2"
|
||||
bcrypt = "0.8"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"]}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
actix = "0.10"
|
||||
actix-web = { version = "3.0", default-features = false }
|
||||
actix-rt = { version = "1.1", default-features = false }
|
||||
awc = { version = "2.0", default-features = false }
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
strum = "0.19"
|
||||
strum_macros = "0.19"
|
||||
lazy_static = "1.3"
|
||||
url = { version = "2.1", features = ["serde"] }
|
||||
percent-encoding = "2.1"
|
||||
openssl = "0.10"
|
||||
http = "0.2"
|
||||
http-signature-normalization-actix = { version = "0.4", default-features = false, features = ["sha-2"] }
|
||||
base64 = "0.12"
|
||||
tokio = "0.2"
|
||||
futures = "0.3"
|
||||
itertools = "0.9"
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
sha2 = "0.9"
|
||||
async-trait = "0.1"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
background-jobs = " 0.8"
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
@ -1,13 +1,11 @@
|
||||
use crate::{
|
||||
apub::{activity_queue::send_activity, community::do_announce, insert_activity},
|
||||
LemmyContext,
|
||||
};
|
||||
use crate::{activity_queue::send_activity, community::do_announce, insert_activity};
|
||||
use activitystreams::{
|
||||
base::{Extends, ExtendsExt},
|
||||
object::AsObject,
|
||||
};
|
||||
use lemmy_db::{community::Community, user::User_};
|
||||
use lemmy_utils::{apub::get_apub_protocol_string, settings::Settings, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::{export::fmt::Debug, Serialize};
|
||||
use url::{ParseError, Url};
|
||||
use uuid::Uuid;
|
||||
@ -37,7 +35,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(in crate::apub) fn generate_activity_id<T>(kind: T) -> Result<Url, ParseError>
|
||||
pub(in crate) fn generate_activity_id<T>(kind: T) -> Result<Url, ParseError>
|
||||
where
|
||||
T: ToString,
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
use crate::apub::{check_is_apub_id_valid, extensions::signatures::sign, ActorType};
|
||||
use crate::{check_is_apub_id_valid, extensions::signatures::sign, ActorType};
|
||||
use activitystreams::{
|
||||
base::{Extends, ExtendsExt},
|
||||
object::AsObject,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
activities::{generate_activity_id, send_activity_to_community},
|
||||
check_actor_domain,
|
||||
create_apub_response,
|
||||
@ -16,9 +15,6 @@ use crate::{
|
||||
ApubObjectType,
|
||||
FromApub,
|
||||
ToApub,
|
||||
},
|
||||
DbPool,
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{
|
||||
@ -46,6 +42,7 @@ use lemmy_db::{
|
||||
post::Post,
|
||||
user::User_,
|
||||
Crud,
|
||||
DbPool,
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{
|
||||
@ -53,6 +50,7 @@ use lemmy_utils::{
|
||||
utils::{convert_datetime, remove_slurs, scrape_text_for_mentions, MentionData},
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use log::debug;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Error;
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
activities::generate_activity_id,
|
||||
activity_queue::send_activity,
|
||||
check_actor_domain,
|
||||
@ -13,9 +12,6 @@ use crate::{
|
||||
FromApub,
|
||||
GroupExt,
|
||||
ToApub,
|
||||
},
|
||||
DbPool,
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{
|
||||
@ -44,6 +40,7 @@ use lemmy_db::{
|
||||
naive_now,
|
||||
post::Post,
|
||||
user::User_,
|
||||
DbPool,
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{
|
||||
@ -52,6 +49,7 @@ use lemmy_utils::{
|
||||
utils::{check_slurs, check_slurs_opt, convert_datetime},
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::apub::ActorType;
|
||||
use crate::ActorType;
|
||||
use activitystreams::unparsed::UnparsedMutExt;
|
||||
use activitystreams_ext::UnparsedExtension;
|
||||
use actix_web::{client::ClientRequest, HttpRequest};
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
check_is_apub_id_valid,
|
||||
ActorType,
|
||||
FromApub,
|
||||
@ -7,9 +6,6 @@ use crate::{
|
||||
PageExt,
|
||||
PersonExt,
|
||||
APUB_JSON_CONTENT_TYPE,
|
||||
},
|
||||
request::{retry, RecvError},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{base::BaseExt, collection::OrderedCollection, object::Note, prelude::*};
|
||||
use anyhow::{anyhow, Context};
|
||||
@ -30,7 +26,13 @@ use lemmy_db::{
|
||||
SearchType,
|
||||
};
|
||||
use lemmy_structs::{blocking, site::SearchResponse};
|
||||
use lemmy_utils::{apub::get_apub_protocol_string, location_info, LemmyError};
|
||||
use lemmy_utils::{
|
||||
apub::get_apub_protocol_string,
|
||||
location_info,
|
||||
request::{retry, RecvError},
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use log::debug;
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::inbox::{
|
||||
use crate::inbox::{
|
||||
activities::{
|
||||
create::receive_create,
|
||||
delete::receive_delete,
|
||||
@ -10,8 +9,6 @@ use crate::{
|
||||
update::receive_update,
|
||||
},
|
||||
shared_inbox::{get_community_id_from_activity, receive_unhandled_activity},
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::*,
|
||||
@ -21,6 +18,7 @@ use activitystreams::{
|
||||
use actix_web::HttpResponse;
|
||||
use anyhow::Context;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
||||
pub async fn receive_announce(
|
||||
activity: AnyBase,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
inbox::shared_inbox::{
|
||||
announce_if_community_is_local,
|
||||
get_user_from_activity,
|
||||
@ -8,8 +7,6 @@ use crate::{
|
||||
ActorType,
|
||||
FromApub,
|
||||
PageExt,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{activity::Create, base::AnyBase, object::Note, prelude::*};
|
||||
use actix_web::HttpResponse;
|
||||
@ -20,14 +17,13 @@ use lemmy_db::{
|
||||
post::{Post, PostForm},
|
||||
post_view::PostView,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
comment::CommentResponse,
|
||||
post::PostResponse,
|
||||
send_local_notifs,
|
||||
websocket::{SendComment, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, comment::CommentResponse, post::PostResponse, send_local_notifs};
|
||||
use lemmy_utils::{location_info, utils::scrape_text_for_mentions, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
messages::{SendComment, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
|
||||
pub async fn receive_create(
|
||||
activity: AnyBase,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
inbox::shared_inbox::{
|
||||
announce_if_community_is_local,
|
||||
@ -10,8 +9,6 @@ use crate::{
|
||||
FromApub,
|
||||
GroupExt,
|
||||
PageExt,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{activity::Delete, base::AnyBase, object::Note, prelude::*};
|
||||
use actix_web::HttpResponse;
|
||||
@ -31,9 +28,13 @@ use lemmy_structs::{
|
||||
comment::CommentResponse,
|
||||
community::CommunityResponse,
|
||||
post::PostResponse,
|
||||
websocket::{SendComment, SendCommunityRoomMessage, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
messages::{SendComment, SendCommunityRoomMessage, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
|
||||
pub async fn receive_delete(
|
||||
activity: AnyBase,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
inbox::shared_inbox::{
|
||||
announce_if_community_is_local,
|
||||
@ -8,8 +7,6 @@ use crate::{
|
||||
},
|
||||
FromApub,
|
||||
PageExt,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{activity::Dislike, base::AnyBase, object::Note, prelude::*};
|
||||
use actix_web::HttpResponse;
|
||||
@ -21,13 +18,13 @@ use lemmy_db::{
|
||||
post_view::PostView,
|
||||
Likeable,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
comment::CommentResponse,
|
||||
post::PostResponse,
|
||||
websocket::{SendComment, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, comment::CommentResponse, post::PostResponse};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
messages::{SendComment, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
|
||||
pub async fn receive_dislike(
|
||||
activity: AnyBase,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
inbox::shared_inbox::{
|
||||
announce_if_community_is_local,
|
||||
@ -8,8 +7,6 @@ use crate::{
|
||||
},
|
||||
FromApub,
|
||||
PageExt,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{activity::Like, base::AnyBase, object::Note, prelude::*};
|
||||
use actix_web::HttpResponse;
|
||||
@ -21,13 +18,13 @@ use lemmy_db::{
|
||||
post_view::PostView,
|
||||
Likeable,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
comment::CommentResponse,
|
||||
post::PostResponse,
|
||||
websocket::{SendComment, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, comment::CommentResponse, post::PostResponse};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
messages::{SendComment, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
|
||||
pub async fn receive_like(
|
||||
activity: AnyBase,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
inbox::shared_inbox::{
|
||||
announce_if_community_is_local,
|
||||
@ -11,8 +10,6 @@ use crate::{
|
||||
FromApub,
|
||||
GroupExt,
|
||||
PageExt,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{activity::Remove, base::AnyBase, object::Note, prelude::*};
|
||||
use actix_web::HttpResponse;
|
||||
@ -32,9 +29,13 @@ use lemmy_structs::{
|
||||
comment::CommentResponse,
|
||||
community::CommunityResponse,
|
||||
post::PostResponse,
|
||||
websocket::{SendComment, SendCommunityRoomMessage, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
messages::{SendComment, SendCommunityRoomMessage, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
|
||||
pub async fn receive_remove(
|
||||
activity: AnyBase,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
inbox::shared_inbox::{
|
||||
announce_if_community_is_local,
|
||||
@ -10,8 +9,6 @@ use crate::{
|
||||
FromApub,
|
||||
GroupExt,
|
||||
PageExt,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::*,
|
||||
@ -37,9 +34,13 @@ use lemmy_structs::{
|
||||
comment::CommentResponse,
|
||||
community::CommunityResponse,
|
||||
post::PostResponse,
|
||||
websocket::{SendComment, SendCommunityRoomMessage, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
messages::{SendComment, SendCommunityRoomMessage, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
|
||||
pub async fn receive_undo(
|
||||
activity: AnyBase,
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||
inbox::shared_inbox::{
|
||||
announce_if_community_is_local,
|
||||
@ -9,8 +8,6 @@ use crate::{
|
||||
ActorType,
|
||||
FromApub,
|
||||
PageExt,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{activity::Update, base::AnyBase, object::Note, prelude::*};
|
||||
use actix_web::HttpResponse;
|
||||
@ -22,14 +19,13 @@ use lemmy_db::{
|
||||
post_view::PostView,
|
||||
Crud,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
comment::CommentResponse,
|
||||
post::PostResponse,
|
||||
send_local_notifs,
|
||||
websocket::{SendComment, SendPost, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, comment::CommentResponse, post::PostResponse, send_local_notifs};
|
||||
use lemmy_utils::{location_info, utils::scrape_text_for_mentions, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
messages::{SendComment, SendPost},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
};
|
||||
|
||||
pub async fn receive_update(
|
||||
activity: AnyBase,
|
@ -1,12 +1,9 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
check_is_apub_id_valid,
|
||||
extensions::signatures::verify,
|
||||
fetcher::get_or_fetch_and_upsert_user,
|
||||
insert_activity,
|
||||
ActorType,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{ActorAndObject, Follow, Undo},
|
||||
@ -22,6 +19,7 @@ use lemmy_db::{
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
check_is_apub_id_valid,
|
||||
community::do_announce,
|
||||
extensions::signatures::verify,
|
||||
@ -19,8 +18,6 @@ use crate::{
|
||||
update::receive_update,
|
||||
},
|
||||
insert_activity,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{ActorAndObject, ActorAndObjectRef},
|
||||
@ -32,6 +29,7 @@ use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use anyhow::Context;
|
||||
use lemmy_db::user::User_;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
@ -97,7 +95,7 @@ pub async fn shared_inbox(
|
||||
res
|
||||
}
|
||||
|
||||
pub(in crate::apub::inbox) fn receive_unhandled_activity<A>(
|
||||
pub(in crate::inbox) fn receive_unhandled_activity<A>(
|
||||
activity: A,
|
||||
) -> Result<HttpResponse, LemmyError>
|
||||
where
|
||||
@ -107,7 +105,7 @@ where
|
||||
Ok(HttpResponse::NotImplemented().finish())
|
||||
}
|
||||
|
||||
pub(in crate::apub::inbox) async fn get_user_from_activity<T, A>(
|
||||
pub(in crate::inbox) async fn get_user_from_activity<T, A>(
|
||||
activity: &T,
|
||||
context: &LemmyContext,
|
||||
) -> Result<User_, LemmyError>
|
||||
@ -119,7 +117,7 @@ where
|
||||
get_or_fetch_and_upsert_user(&user_uri, context).await
|
||||
}
|
||||
|
||||
pub(in crate::apub::inbox) fn get_community_id_from_activity<T, A>(
|
||||
pub(in crate::inbox) fn get_community_id_from_activity<T, A>(
|
||||
activity: &T,
|
||||
) -> Result<Url, LemmyError>
|
||||
where
|
||||
@ -136,7 +134,7 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
pub(in crate::apub::inbox) async fn announce_if_community_is_local<T, Kind>(
|
||||
pub(in crate::inbox) async fn announce_if_community_is_local<T, Kind>(
|
||||
activity: T,
|
||||
user: &User_,
|
||||
context: &LemmyContext,
|
@ -1,12 +1,9 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
check_is_apub_id_valid,
|
||||
extensions::signatures::verify,
|
||||
fetcher::{get_or_fetch_and_upsert_actor, get_or_fetch_and_upsert_community},
|
||||
insert_activity,
|
||||
FromApub,
|
||||
},
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{Accept, ActorAndObject, Create, Delete, Undo, Update},
|
||||
@ -25,12 +22,9 @@ use lemmy_db::{
|
||||
Crud,
|
||||
Followable,
|
||||
};
|
||||
use lemmy_structs::{
|
||||
blocking,
|
||||
user::PrivateMessageResponse,
|
||||
websocket::{SendUserRoomMessage, UserOperation},
|
||||
};
|
||||
use lemmy_structs::{blocking, user::PrivateMessageResponse};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperation};
|
||||
use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
@ -1,3 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
pub mod activities;
|
||||
pub mod activity_queue;
|
||||
pub mod comment;
|
||||
@ -9,16 +12,10 @@ pub mod post;
|
||||
pub mod private_message;
|
||||
pub mod user;
|
||||
|
||||
use crate::{
|
||||
apub::extensions::{
|
||||
use crate::extensions::{
|
||||
group_extensions::GroupExtension,
|
||||
page_extension::PageExtension,
|
||||
signatures::{PublicKey, PublicKeyExtension},
|
||||
},
|
||||
request::{retry, RecvError},
|
||||
routes::webfinger::WebFingerResponse,
|
||||
DbPool,
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::Follow,
|
||||
@ -32,15 +29,17 @@ use activitystreams_ext::{Ext1, Ext2};
|
||||
use actix_web::{body::Body, HttpResponse};
|
||||
use anyhow::{anyhow, Context};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_db::{activity::do_insert_activity, user::User_};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_db::{activity::do_insert_activity, user::User_, DbPool};
|
||||
use lemmy_structs::{blocking, WebFingerResponse};
|
||||
use lemmy_utils::{
|
||||
apub::get_apub_protocol_string,
|
||||
location_info,
|
||||
request::{retry, RecvError},
|
||||
settings::Settings,
|
||||
utils::{convert_datetime, MentionData},
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use log::debug;
|
||||
use reqwest::Client;
|
||||
use serde::Serialize;
|
||||
@ -191,7 +190,7 @@ pub trait ApubObjectType {
|
||||
async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||
}
|
||||
|
||||
pub(in crate::apub) fn check_actor_domain<T, Kind>(
|
||||
pub(in crate) fn check_actor_domain<T, Kind>(
|
||||
apub: &T,
|
||||
expected_domain: Option<Url>,
|
||||
) -> Result<String, LemmyError>
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
activities::{generate_activity_id, send_activity_to_community},
|
||||
check_actor_domain,
|
||||
create_apub_response,
|
||||
@ -13,9 +12,6 @@ use crate::{
|
||||
FromApub,
|
||||
PageExt,
|
||||
ToApub,
|
||||
},
|
||||
DbPool,
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{
|
||||
@ -40,6 +36,7 @@ use lemmy_db::{
|
||||
post::{Post, PostForm},
|
||||
user::User_,
|
||||
Crud,
|
||||
DbPool,
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{
|
||||
@ -47,6 +44,7 @@ use lemmy_utils::{
|
||||
utils::{check_slurs, convert_datetime, remove_slurs},
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
activities::generate_activity_id,
|
||||
activity_queue::send_activity,
|
||||
check_actor_domain,
|
||||
@ -11,9 +10,6 @@ use crate::{
|
||||
ApubObjectType,
|
||||
FromApub,
|
||||
ToApub,
|
||||
},
|
||||
DbPool,
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{
|
||||
@ -31,9 +27,11 @@ use lemmy_db::{
|
||||
private_message::{PrivateMessage, PrivateMessageForm},
|
||||
user::User_,
|
||||
Crud,
|
||||
DbPool,
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{location_info, utils::convert_datetime, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
apub::{
|
||||
activities::generate_activity_id,
|
||||
activity_queue::send_activity,
|
||||
check_actor_domain,
|
||||
@ -10,9 +9,6 @@ use crate::{
|
||||
FromApub,
|
||||
PersonExt,
|
||||
ToApub,
|
||||
},
|
||||
DbPool,
|
||||
LemmyContext,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{
|
||||
@ -30,6 +26,7 @@ use anyhow::Context;
|
||||
use lemmy_db::{
|
||||
naive_now,
|
||||
user::{UserForm, User_},
|
||||
DbPool,
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{
|
||||
@ -37,6 +34,7 @@ use lemmy_utils::{
|
||||
utils::{check_slurs, check_slurs_opt, convert_datetime},
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
@ -4,14 +4,6 @@ extern crate diesel;
|
||||
extern crate strum_macros;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate bcrypt;
|
||||
extern crate chrono;
|
||||
extern crate log;
|
||||
extern crate regex;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate sha2;
|
||||
extern crate strum;
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{result::Error, *};
|
||||
|
@ -1,9 +1,5 @@
|
||||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
extern crate actix_web;
|
||||
extern crate futures;
|
||||
extern crate log;
|
||||
extern crate tokio;
|
||||
|
||||
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
||||
use futures::future::{ok, Ready};
|
||||
|
@ -14,8 +14,6 @@ lemmy_utils = { path = "../lemmy_utils" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
log = "0.4"
|
||||
diesel = "1.4"
|
||||
actix = "0.10"
|
||||
actix-web = { version = "3.0" }
|
||||
strum = "0.19"
|
||||
strum_macros = "0.19"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"]}
|
||||
|
@ -1,12 +1,3 @@
|
||||
extern crate actix;
|
||||
extern crate actix_web;
|
||||
extern crate diesel;
|
||||
extern crate log;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
extern crate chrono;
|
||||
|
||||
pub mod comment;
|
||||
pub mod community;
|
||||
pub mod post;
|
||||
@ -25,6 +16,24 @@ use lemmy_db::{
|
||||
};
|
||||
use lemmy_utils::{email::send_email, settings::Settings, utils::MentionData, LemmyError};
|
||||
use log::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WebFingerLink {
|
||||
pub rel: Option<String>,
|
||||
#[serde(rename(serialize = "type", deserialize = "type"))]
|
||||
pub type_: Option<String>,
|
||||
pub href: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WebFingerResponse {
|
||||
pub subject: String,
|
||||
pub aliases: Vec<String>,
|
||||
pub links: Vec<WebFingerLink>,
|
||||
}
|
||||
|
||||
pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
|
||||
where
|
||||
|
@ -1,195 +1 @@
|
||||
use crate::{comment::CommentResponse, post::PostResponse};
|
||||
use actix::{prelude::*, Recipient};
|
||||
use lemmy_utils::{CommunityId, ConnectionId, IPAddr, PostId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(EnumString, ToString, Debug, Clone)]
|
||||
pub enum UserOperation {
|
||||
Login,
|
||||
Register,
|
||||
GetCaptcha,
|
||||
CreateCommunity,
|
||||
CreatePost,
|
||||
ListCommunities,
|
||||
ListCategories,
|
||||
GetPost,
|
||||
GetCommunity,
|
||||
CreateComment,
|
||||
EditComment,
|
||||
DeleteComment,
|
||||
RemoveComment,
|
||||
MarkCommentAsRead,
|
||||
SaveComment,
|
||||
CreateCommentLike,
|
||||
GetPosts,
|
||||
CreatePostLike,
|
||||
EditPost,
|
||||
DeletePost,
|
||||
RemovePost,
|
||||
LockPost,
|
||||
StickyPost,
|
||||
SavePost,
|
||||
EditCommunity,
|
||||
DeleteCommunity,
|
||||
RemoveCommunity,
|
||||
FollowCommunity,
|
||||
GetFollowedCommunities,
|
||||
GetUserDetails,
|
||||
GetReplies,
|
||||
GetUserMentions,
|
||||
MarkUserMentionAsRead,
|
||||
GetModlog,
|
||||
BanFromCommunity,
|
||||
AddModToCommunity,
|
||||
CreateSite,
|
||||
EditSite,
|
||||
GetSite,
|
||||
AddAdmin,
|
||||
BanUser,
|
||||
Search,
|
||||
MarkAllAsRead,
|
||||
SaveUserSettings,
|
||||
TransferCommunity,
|
||||
TransferSite,
|
||||
DeleteAccount,
|
||||
PasswordReset,
|
||||
PasswordChange,
|
||||
CreatePrivateMessage,
|
||||
EditPrivateMessage,
|
||||
DeletePrivateMessage,
|
||||
MarkPrivateMessageAsRead,
|
||||
GetPrivateMessages,
|
||||
UserJoin,
|
||||
GetComments,
|
||||
GetSiteConfig,
|
||||
SaveSiteConfig,
|
||||
PostJoin,
|
||||
CommunityJoin,
|
||||
}
|
||||
|
||||
/// Chat server sends this messages to session
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct WSMessage(pub String);
|
||||
|
||||
/// Message for chat server communications
|
||||
|
||||
/// New chat session is created
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct Connect {
|
||||
pub addr: Recipient<WSMessage>,
|
||||
pub ip: IPAddr,
|
||||
}
|
||||
|
||||
/// Session is disconnected
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct Disconnect {
|
||||
pub id: ConnectionId,
|
||||
pub ip: IPAddr,
|
||||
}
|
||||
|
||||
/// The messages sent to websocket clients
|
||||
#[derive(Serialize, Deserialize, Message)]
|
||||
#[rtype(result = "Result<String, std::convert::Infallible>")]
|
||||
pub struct StandardMessage {
|
||||
/// Id of the client session
|
||||
pub id: ConnectionId,
|
||||
/// Peer message
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendAllMessage<Response> {
|
||||
pub op: UserOperation,
|
||||
pub response: Response,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendUserRoomMessage<Response> {
|
||||
pub op: UserOperation,
|
||||
pub response: Response,
|
||||
pub recipient_id: UserId,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendCommunityRoomMessage<Response> {
|
||||
pub op: UserOperation,
|
||||
pub response: Response,
|
||||
pub community_id: CommunityId,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendPost {
|
||||
pub op: UserOperation,
|
||||
pub post: PostResponse,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendComment {
|
||||
pub op: UserOperation,
|
||||
pub comment: CommentResponse,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinUserRoom {
|
||||
pub user_id: UserId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinCommunityRoom {
|
||||
pub community_id: CommunityId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinPostRoom {
|
||||
pub post_id: PostId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetUsersOnline;
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetPostUsersOnline {
|
||||
pub post_id: PostId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetCommunityUsersOnline {
|
||||
pub community_id: CommunityId,
|
||||
}
|
||||
|
||||
#[derive(Message, Debug)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct CaptchaItem {
|
||||
pub uuid: String,
|
||||
pub answer: String,
|
||||
pub expires: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(bool)]
|
||||
pub struct CheckCaptcha {
|
||||
pub uuid: String,
|
||||
pub answer: String,
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ edition = "2018"
|
||||
name = "lemmy_utils"
|
||||
path = "src/lib.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
regex = "1.3"
|
||||
config = { version = "0.10", default-features = false, features = ["hjson"] }
|
||||
@ -27,3 +25,4 @@ openssl = "0.10"
|
||||
url = { version = "2.1", features = ["serde"] }
|
||||
actix-web = { version = "3.0", default-features = false, features = ["rustls"] }
|
||||
anyhow = "1.0"
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
|
@ -1,19 +1,9 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate actix_web;
|
||||
extern crate anyhow;
|
||||
extern crate comrak;
|
||||
extern crate lettre;
|
||||
extern crate lettre_email;
|
||||
extern crate openssl;
|
||||
extern crate rand;
|
||||
extern crate regex;
|
||||
extern crate serde_json;
|
||||
extern crate thiserror;
|
||||
extern crate url;
|
||||
|
||||
pub mod apub;
|
||||
pub mod email;
|
||||
pub mod request;
|
||||
pub mod settings;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::LemmyError;
|
||||
use anyhow::anyhow;
|
||||
use lemmy_utils::LemmyError;
|
||||
use std::future::Future;
|
||||
use thiserror::Error;
|
||||
|
28
lemmy_websocket/Cargo.toml
Normal file
28
lemmy_websocket/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "lemmy_websocket"
|
||||
version = "0.1.0"
|
||||
authors = ["Felix Ableitner <me@nutomic.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "lemmy_websocket"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { path = "../lemmy_utils" }
|
||||
lemmy_structs = { path = "../lemmy_structs" }
|
||||
lemmy_db = { path = "../lemmy_db" }
|
||||
lemmy_rate_limit = { path = "../lemmy_rate_limit" }
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"]}
|
||||
actix = "0.10"
|
||||
anyhow = "1.0"
|
||||
diesel = "1.4"
|
||||
background-jobs = " 0.8"
|
||||
tokio = "0.2"
|
||||
strum = "0.19"
|
||||
strum_macros = "0.19"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
websocket::handlers::{do_user_operation, to_json_string, Args},
|
||||
LemmyContext,
|
||||
};
|
||||
use crate::{messages::*, serialize_websocket_message, LemmyContext, UserOperation};
|
||||
use actix::prelude::*;
|
||||
use anyhow::Context as acontext;
|
||||
use background_jobs::QueueHandle;
|
||||
@ -10,7 +7,7 @@ use diesel::{
|
||||
PgConnection,
|
||||
};
|
||||
use lemmy_rate_limit::RateLimit;
|
||||
use lemmy_structs::{comment::*, community::*, post::*, site::*, user::*, websocket::*};
|
||||
use lemmy_structs::{comment::*, post::*};
|
||||
use lemmy_utils::{
|
||||
location_info,
|
||||
APIError,
|
||||
@ -29,6 +26,14 @@ use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
str::FromStr,
|
||||
};
|
||||
use tokio::macros::support::Pin;
|
||||
|
||||
type MessageHandlerType = fn(
|
||||
context: LemmyContext,
|
||||
id: ConnectionId,
|
||||
op: UserOperation,
|
||||
data: &str,
|
||||
) -> Pin<Box<dyn Future<Output = Result<String, LemmyError>> + '_>>;
|
||||
|
||||
/// `ChatServer` manages chat rooms and responsible for coordinating chat
|
||||
/// session.
|
||||
@ -57,6 +62,8 @@ pub struct ChatServer {
|
||||
/// A list of the current captchas
|
||||
pub(super) captchas: Vec<CaptchaItem>,
|
||||
|
||||
message_handler: MessageHandlerType,
|
||||
|
||||
/// An HTTP Client
|
||||
client: Client,
|
||||
|
||||
@ -75,6 +82,7 @@ impl ChatServer {
|
||||
pub fn startup(
|
||||
pool: Pool<ConnectionManager<PgConnection>>,
|
||||
rate_limiter: RateLimit,
|
||||
message_handler: MessageHandlerType,
|
||||
client: Client,
|
||||
activity_queue: QueueHandle,
|
||||
) -> ChatServer {
|
||||
@ -87,6 +95,7 @@ impl ChatServer {
|
||||
pool,
|
||||
rate_limiter,
|
||||
captchas: Vec::new(),
|
||||
message_handler,
|
||||
client,
|
||||
activity_queue,
|
||||
}
|
||||
@ -180,7 +189,7 @@ impl ChatServer {
|
||||
where
|
||||
Response: Serialize,
|
||||
{
|
||||
let res_str = &to_json_string(op, response)?;
|
||||
let res_str = &serialize_websocket_message(op, response)?;
|
||||
if let Some(sessions) = self.post_rooms.get(&post_id) {
|
||||
for id in sessions {
|
||||
if let Some(my_id) = websocket_id {
|
||||
@ -204,7 +213,7 @@ impl ChatServer {
|
||||
where
|
||||
Response: Serialize,
|
||||
{
|
||||
let res_str = &to_json_string(op, response)?;
|
||||
let res_str = &serialize_websocket_message(op, response)?;
|
||||
if let Some(sessions) = self.community_rooms.get(&community_id) {
|
||||
for id in sessions {
|
||||
if let Some(my_id) = websocket_id {
|
||||
@ -227,7 +236,7 @@ impl ChatServer {
|
||||
where
|
||||
Response: Serialize,
|
||||
{
|
||||
let res_str = &to_json_string(op, response)?;
|
||||
let res_str = &serialize_websocket_message(op, response)?;
|
||||
for id in self.sessions.keys() {
|
||||
if let Some(my_id) = websocket_id {
|
||||
if *id == my_id {
|
||||
@ -249,7 +258,7 @@ impl ChatServer {
|
||||
where
|
||||
Response: Serialize,
|
||||
{
|
||||
let res_str = &to_json_string(op, response)?;
|
||||
let res_str = &serialize_websocket_message(op, response)?;
|
||||
if let Some(sessions) = self.user_rooms.get(&recipient_id) {
|
||||
for id in sessions {
|
||||
if let Some(my_id) = websocket_id {
|
||||
@ -340,8 +349,6 @@ impl ChatServer {
|
||||
msg: StandardMessage,
|
||||
ctx: &mut Context<Self>,
|
||||
) -> impl Future<Output = Result<String, LemmyError>> {
|
||||
let addr = ctx.address();
|
||||
let pool = self.pool.clone();
|
||||
let rate_limiter = self.rate_limiter.clone();
|
||||
|
||||
let ip: IPAddr = match self.sessions.get(&msg.id) {
|
||||
@ -349,110 +356,27 @@ impl ChatServer {
|
||||
None => "blank_ip".to_string(),
|
||||
};
|
||||
|
||||
let client = self.client.clone();
|
||||
let activity_queue = self.activity_queue.clone();
|
||||
let context = LemmyContext {
|
||||
pool: self.pool.clone(),
|
||||
chat_server: ctx.address(),
|
||||
client: self.client.to_owned(),
|
||||
activity_queue: self.activity_queue.to_owned(),
|
||||
};
|
||||
let message_handler = self.message_handler;
|
||||
async move {
|
||||
let msg = msg;
|
||||
let json: Value = serde_json::from_str(&msg.msg)?;
|
||||
let data = &json["data"].to_string();
|
||||
let op = &json["op"].as_str().ok_or(APIError {
|
||||
message: "Unknown op type".to_string(),
|
||||
})?;
|
||||
|
||||
let user_operation: UserOperation = UserOperation::from_str(&op)?;
|
||||
|
||||
let context = LemmyContext::new(pool, addr, client, activity_queue);
|
||||
let args = Args {
|
||||
context,
|
||||
rate_limiter,
|
||||
id: msg.id,
|
||||
ip,
|
||||
op: user_operation.clone(),
|
||||
data,
|
||||
};
|
||||
|
||||
let user_operation = UserOperation::from_str(&op)?;
|
||||
let fut = (message_handler)(context, msg.id, user_operation.clone(), data);
|
||||
match user_operation {
|
||||
// User ops
|
||||
UserOperation::Login => do_user_operation::<Login>(args).await,
|
||||
UserOperation::Register => do_user_operation::<Register>(args).await,
|
||||
UserOperation::GetCaptcha => do_user_operation::<GetCaptcha>(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::MarkUserMentionAsRead => {
|
||||
do_user_operation::<MarkUserMentionAsRead>(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::UserJoin => do_user_operation::<UserJoin>(args).await,
|
||||
UserOperation::PostJoin => do_user_operation::<PostJoin>(args).await,
|
||||
UserOperation::CommunityJoin => do_user_operation::<CommunityJoin>(args).await,
|
||||
UserOperation::SaveUserSettings => do_user_operation::<SaveUserSettings>(args).await,
|
||||
|
||||
// Private Message ops
|
||||
UserOperation::CreatePrivateMessage => {
|
||||
do_user_operation::<CreatePrivateMessage>(args).await
|
||||
}
|
||||
UserOperation::EditPrivateMessage => do_user_operation::<EditPrivateMessage>(args).await,
|
||||
UserOperation::DeletePrivateMessage => {
|
||||
do_user_operation::<DeletePrivateMessage>(args).await
|
||||
}
|
||||
UserOperation::MarkPrivateMessageAsRead => {
|
||||
do_user_operation::<MarkPrivateMessageAsRead>(args).await
|
||||
}
|
||||
UserOperation::GetPrivateMessages => do_user_operation::<GetPrivateMessages>(args).await,
|
||||
|
||||
// Site ops
|
||||
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>(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::DeleteCommunity => do_user_operation::<DeleteCommunity>(args).await,
|
||||
UserOperation::RemoveCommunity => do_user_operation::<RemoveCommunity>(args).await,
|
||||
UserOperation::FollowCommunity => do_user_operation::<FollowCommunity>(args).await,
|
||||
UserOperation::GetFollowedCommunities => {
|
||||
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>(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::DeletePost => do_user_operation::<DeletePost>(args).await,
|
||||
UserOperation::RemovePost => do_user_operation::<RemovePost>(args).await,
|
||||
UserOperation::LockPost => do_user_operation::<LockPost>(args).await,
|
||||
UserOperation::StickyPost => do_user_operation::<StickyPost>(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>(args).await,
|
||||
UserOperation::EditComment => do_user_operation::<EditComment>(args).await,
|
||||
UserOperation::DeleteComment => do_user_operation::<DeleteComment>(args).await,
|
||||
UserOperation::RemoveComment => do_user_operation::<RemoveComment>(args).await,
|
||||
UserOperation::MarkCommentAsRead => do_user_operation::<MarkCommentAsRead>(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,
|
||||
UserOperation::Register => rate_limiter.register().wrap(ip, fut).await,
|
||||
UserOperation::CreatePost => rate_limiter.post().wrap(ip, fut).await,
|
||||
UserOperation::CreateCommunity => rate_limiter.register().wrap(ip, fut).await,
|
||||
_ => rate_limiter.message().wrap(ip, fut).await,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +1,12 @@
|
||||
use crate::{
|
||||
api::Perform,
|
||||
websocket::chat_server::{ChatServer, SessionInfo},
|
||||
LemmyContext,
|
||||
chat_server::{ChatServer, SessionInfo},
|
||||
messages::*,
|
||||
};
|
||||
use actix::{Actor, Context, Handler, ResponseFuture};
|
||||
use actix_web::web;
|
||||
use lemmy_db::naive_now;
|
||||
use lemmy_rate_limit::RateLimit;
|
||||
use lemmy_structs::websocket::*;
|
||||
use lemmy_utils::{ConnectionId, IPAddr, LemmyError};
|
||||
use log::{error, info};
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub(super) struct Args<'a> {
|
||||
pub(super) context: LemmyContext,
|
||||
pub(super) rate_limiter: RateLimit,
|
||||
pub(super) id: ConnectionId,
|
||||
pub(super) ip: IPAddr,
|
||||
pub(super) op: UserOperation,
|
||||
pub(super) data: &'a str,
|
||||
}
|
||||
|
||||
pub(super) async fn do_user_operation<'a, 'b, Data>(args: Args<'b>) -> Result<String, LemmyError>
|
||||
where
|
||||
for<'de> Data: Deserialize<'de> + 'a,
|
||||
Data: Perform,
|
||||
{
|
||||
let Args {
|
||||
context,
|
||||
rate_limiter,
|
||||
id,
|
||||
ip,
|
||||
op,
|
||||
data,
|
||||
} = args;
|
||||
|
||||
let data = data.to_string();
|
||||
let op2 = op.clone();
|
||||
|
||||
let fut = async move {
|
||||
let parsed_data: Data = serde_json::from_str(&data)?;
|
||||
let res = parsed_data
|
||||
.perform(&web::Data::new(context), Some(id))
|
||||
.await?;
|
||||
to_json_string(&op, &res)
|
||||
};
|
||||
|
||||
match op2 {
|
||||
UserOperation::Register => rate_limiter.register().wrap(ip, fut).await,
|
||||
UserOperation::CreatePost => rate_limiter.post().wrap(ip, fut).await,
|
||||
UserOperation::CreateCommunity => rate_limiter.register().wrap(ip, fut).await,
|
||||
_ => rate_limiter.message().wrap(ip, fut).await,
|
||||
}
|
||||
}
|
||||
use serde::Serialize;
|
||||
|
||||
/// Make actor from `ChatServer`
|
||||
impl Actor for ChatServer {
|
||||
@ -241,26 +194,6 @@ impl Handler<GetCommunityUsersOnline> for ChatServer {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WebsocketResponse<T> {
|
||||
op: String,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub(super) fn to_json_string<Response>(
|
||||
op: &UserOperation,
|
||||
data: &Response,
|
||||
) -> Result<String, LemmyError>
|
||||
where
|
||||
Response: Serialize,
|
||||
{
|
||||
let response = WebsocketResponse {
|
||||
op: op.to_string(),
|
||||
data,
|
||||
};
|
||||
Ok(serde_json::to_string(&response)?)
|
||||
}
|
||||
|
||||
impl Handler<CaptchaItem> for ChatServer {
|
||||
type Result = ();
|
||||
|
144
lemmy_websocket/src/lib.rs
Normal file
144
lemmy_websocket/src/lib.rs
Normal file
@ -0,0 +1,144 @@
|
||||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
|
||||
use crate::chat_server::ChatServer;
|
||||
use actix::Addr;
|
||||
use background_jobs::QueueHandle;
|
||||
use lemmy_db::DbPool;
|
||||
use lemmy_utils::LemmyError;
|
||||
use reqwest::Client;
|
||||
use serde::Serialize;
|
||||
|
||||
pub mod chat_server;
|
||||
pub mod handlers;
|
||||
pub mod messages;
|
||||
|
||||
pub struct LemmyContext {
|
||||
pub pool: DbPool,
|
||||
pub chat_server: Addr<ChatServer>,
|
||||
pub client: Client,
|
||||
pub activity_queue: QueueHandle,
|
||||
}
|
||||
|
||||
impl LemmyContext {
|
||||
pub fn create(
|
||||
pool: DbPool,
|
||||
chat_server: Addr<ChatServer>,
|
||||
client: Client,
|
||||
activity_queue: QueueHandle,
|
||||
) -> LemmyContext {
|
||||
LemmyContext {
|
||||
pool,
|
||||
chat_server,
|
||||
client,
|
||||
activity_queue,
|
||||
}
|
||||
}
|
||||
pub fn pool(&self) -> &DbPool {
|
||||
&self.pool
|
||||
}
|
||||
pub fn chat_server(&self) -> &Addr<ChatServer> {
|
||||
&self.chat_server
|
||||
}
|
||||
pub fn client(&self) -> &Client {
|
||||
&self.client
|
||||
}
|
||||
pub fn activity_queue(&self) -> &QueueHandle {
|
||||
&self.activity_queue
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for LemmyContext {
|
||||
fn clone(&self) -> Self {
|
||||
LemmyContext {
|
||||
pool: self.pool.clone(),
|
||||
chat_server: self.chat_server.clone(),
|
||||
client: self.client.clone(),
|
||||
activity_queue: self.activity_queue.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WebsocketResponse<T> {
|
||||
op: String,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub fn serialize_websocket_message<Response>(
|
||||
op: &UserOperation,
|
||||
data: &Response,
|
||||
) -> Result<String, LemmyError>
|
||||
where
|
||||
Response: Serialize,
|
||||
{
|
||||
let response = WebsocketResponse {
|
||||
op: op.to_string(),
|
||||
data,
|
||||
};
|
||||
Ok(serde_json::to_string(&response)?)
|
||||
}
|
||||
|
||||
#[derive(EnumString, ToString, Debug, Clone)]
|
||||
pub enum UserOperation {
|
||||
Login,
|
||||
Register,
|
||||
GetCaptcha,
|
||||
CreateCommunity,
|
||||
CreatePost,
|
||||
ListCommunities,
|
||||
ListCategories,
|
||||
GetPost,
|
||||
GetCommunity,
|
||||
CreateComment,
|
||||
EditComment,
|
||||
DeleteComment,
|
||||
RemoveComment,
|
||||
MarkCommentAsRead,
|
||||
SaveComment,
|
||||
CreateCommentLike,
|
||||
GetPosts,
|
||||
CreatePostLike,
|
||||
EditPost,
|
||||
DeletePost,
|
||||
RemovePost,
|
||||
LockPost,
|
||||
StickyPost,
|
||||
SavePost,
|
||||
EditCommunity,
|
||||
DeleteCommunity,
|
||||
RemoveCommunity,
|
||||
FollowCommunity,
|
||||
GetFollowedCommunities,
|
||||
GetUserDetails,
|
||||
GetReplies,
|
||||
GetUserMentions,
|
||||
MarkUserMentionAsRead,
|
||||
GetModlog,
|
||||
BanFromCommunity,
|
||||
AddModToCommunity,
|
||||
CreateSite,
|
||||
EditSite,
|
||||
GetSite,
|
||||
AddAdmin,
|
||||
BanUser,
|
||||
Search,
|
||||
MarkAllAsRead,
|
||||
SaveUserSettings,
|
||||
TransferCommunity,
|
||||
TransferSite,
|
||||
DeleteAccount,
|
||||
PasswordReset,
|
||||
PasswordChange,
|
||||
CreatePrivateMessage,
|
||||
EditPrivateMessage,
|
||||
DeletePrivateMessage,
|
||||
MarkPrivateMessageAsRead,
|
||||
GetPrivateMessages,
|
||||
UserJoin,
|
||||
GetComments,
|
||||
GetSiteConfig,
|
||||
SaveSiteConfig,
|
||||
PostJoin,
|
||||
CommunityJoin,
|
||||
}
|
132
lemmy_websocket/src/messages.rs
Normal file
132
lemmy_websocket/src/messages.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use crate::UserOperation;
|
||||
use actix::{prelude::*, Recipient};
|
||||
use lemmy_structs::{comment::CommentResponse, post::PostResponse};
|
||||
use lemmy_utils::{CommunityId, ConnectionId, IPAddr, PostId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Chat server sends this messages to session
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct WSMessage(pub String);
|
||||
|
||||
/// Message for chat server communications
|
||||
|
||||
/// New chat session is created
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct Connect {
|
||||
pub addr: Recipient<WSMessage>,
|
||||
pub ip: IPAddr,
|
||||
}
|
||||
|
||||
/// Session is disconnected
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct Disconnect {
|
||||
pub id: ConnectionId,
|
||||
pub ip: IPAddr,
|
||||
}
|
||||
|
||||
/// The messages sent to websocket clients
|
||||
#[derive(Serialize, Deserialize, Message)]
|
||||
#[rtype(result = "Result<String, std::convert::Infallible>")]
|
||||
pub struct StandardMessage {
|
||||
/// Id of the client session
|
||||
pub id: ConnectionId,
|
||||
/// Peer message
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendAllMessage<Response> {
|
||||
pub op: UserOperation,
|
||||
pub response: Response,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendUserRoomMessage<Response> {
|
||||
pub op: UserOperation,
|
||||
pub response: Response,
|
||||
pub recipient_id: UserId,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendCommunityRoomMessage<Response> {
|
||||
pub op: UserOperation,
|
||||
pub response: Response,
|
||||
pub community_id: CommunityId,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendPost {
|
||||
pub op: UserOperation,
|
||||
pub post: PostResponse,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct SendComment {
|
||||
pub op: UserOperation,
|
||||
pub comment: CommentResponse,
|
||||
pub websocket_id: Option<ConnectionId>,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinUserRoom {
|
||||
pub user_id: UserId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinCommunityRoom {
|
||||
pub community_id: CommunityId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct JoinPostRoom {
|
||||
pub post_id: PostId,
|
||||
pub id: ConnectionId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetUsersOnline;
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetPostUsersOnline {
|
||||
pub post_id: PostId,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct GetCommunityUsersOnline {
|
||||
pub community_id: CommunityId,
|
||||
}
|
||||
|
||||
#[derive(Message, Debug)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct CaptchaItem {
|
||||
pub uuid: String,
|
||||
pub answer: String,
|
||||
pub expires: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(bool)]
|
||||
pub struct CheckCaptcha {
|
||||
pub uuid: String,
|
||||
pub answer: String,
|
||||
}
|
128
src/api/mod.rs
128
src/api/mod.rs
@ -1,128 +0,0 @@
|
||||
use crate::{api::claims::Claims, DbPool, LemmyContext};
|
||||
use actix_web::web::Data;
|
||||
use lemmy_db::{
|
||||
community::Community,
|
||||
community_view::CommunityUserBanView,
|
||||
post::Post,
|
||||
user::User_,
|
||||
Crud,
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{settings::Settings, APIError, ConnectionId, LemmyError};
|
||||
use url::Url;
|
||||
|
||||
pub mod claims;
|
||||
pub mod comment;
|
||||
pub mod community;
|
||||
pub mod post;
|
||||
pub mod site;
|
||||
pub mod user;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait Perform {
|
||||
type Response: serde::ser::Serialize + Send;
|
||||
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<Self::Response, LemmyError>;
|
||||
}
|
||||
|
||||
pub(in crate::api) async fn is_mod_or_admin(
|
||||
pool: &DbPool,
|
||||
user_id: i32,
|
||||
community_id: i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let is_mod_or_admin = blocking(pool, move |conn| {
|
||||
Community::is_mod_or_admin(conn, user_id, community_id)
|
||||
})
|
||||
.await?;
|
||||
if !is_mod_or_admin {
|
||||
return Err(APIError::err("not_a_mod_or_admin").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub async fn is_admin(pool: &DbPool, user_id: i32) -> Result<(), LemmyError> {
|
||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
||||
if !user.admin {
|
||||
return Err(APIError::err("not_an_admin").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(in crate::api) async fn get_post(post_id: i32, pool: &DbPool) -> Result<Post, LemmyError> {
|
||||
match blocking(pool, move |conn| Post::read(conn, post_id)).await? {
|
||||
Ok(post) => Ok(post),
|
||||
Err(_e) => Err(APIError::err("couldnt_find_post").into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::api) async fn get_user_from_jwt(
|
||||
jwt: &str,
|
||||
pool: &DbPool,
|
||||
) -> Result<User_, LemmyError> {
|
||||
let claims = match Claims::decode(&jwt) {
|
||||
Ok(claims) => claims.claims,
|
||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||
};
|
||||
let user_id = claims.id;
|
||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
||||
// Check for a site ban
|
||||
if user.banned {
|
||||
return Err(APIError::err("site_ban").into());
|
||||
}
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub(in crate::api) async fn get_user_from_jwt_opt(
|
||||
jwt: &Option<String>,
|
||||
pool: &DbPool,
|
||||
) -> Result<Option<User_>, LemmyError> {
|
||||
match jwt {
|
||||
Some(jwt) => Ok(Some(get_user_from_jwt(jwt, pool).await?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::api) async fn check_community_ban(
|
||||
user_id: i32,
|
||||
community_id: i32,
|
||||
pool: &DbPool,
|
||||
) -> Result<(), LemmyError> {
|
||||
let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
||||
if blocking(pool, is_banned).await? {
|
||||
Err(APIError::err("community_ban").into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::api) async fn linked_instances(pool: &DbPool) -> Result<Vec<String>, LemmyError> {
|
||||
let mut instances: Vec<String> = Vec::new();
|
||||
|
||||
if Settings::get().federation.enabled {
|
||||
let distinct_communities = blocking(pool, move |conn| {
|
||||
Community::distinct_federated_communities(conn)
|
||||
})
|
||||
.await??;
|
||||
|
||||
instances = distinct_communities
|
||||
.iter()
|
||||
.map(|actor_id| Ok(Url::parse(actor_id)?.host_str().unwrap_or("").to_string()))
|
||||
.collect::<Result<Vec<String>, LemmyError>>()?;
|
||||
|
||||
instances.append(&mut Settings::get().get_allowed_instances());
|
||||
instances.retain(|a| {
|
||||
!Settings::get().get_blocked_instances().contains(a)
|
||||
&& !a.eq("")
|
||||
&& !a.eq(&Settings::get().hostname)
|
||||
});
|
||||
|
||||
// Sort and remove dupes
|
||||
instances.sort_unstable();
|
||||
instances.dedup();
|
||||
}
|
||||
|
||||
Ok(instances)
|
||||
}
|
303
src/lib.rs
303
src/lib.rs
@ -1,307 +1,4 @@
|
||||
#![recursion_limit = "512"]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate actix;
|
||||
extern crate actix_web;
|
||||
extern crate base64;
|
||||
extern crate bcrypt;
|
||||
extern crate captcha;
|
||||
extern crate chrono;
|
||||
extern crate diesel;
|
||||
extern crate dotenv;
|
||||
extern crate jsonwebtoken;
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate reqwest;
|
||||
extern crate rss;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate sha2;
|
||||
extern crate strum;
|
||||
|
||||
pub mod api;
|
||||
pub mod apub;
|
||||
pub mod code_migrations;
|
||||
pub mod request;
|
||||
pub mod routes;
|
||||
pub mod version;
|
||||
pub mod websocket;
|
||||
|
||||
use crate::{
|
||||
request::{retry, RecvError},
|
||||
websocket::chat_server::ChatServer,
|
||||
};
|
||||
use actix::Addr;
|
||||
use anyhow::anyhow;
|
||||
use background_jobs::QueueHandle;
|
||||
use lemmy_db::DbPool;
|
||||
use lemmy_utils::{apub::get_apub_protocol_string, settings::Settings, LemmyError};
|
||||
use log::error;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use std::process::Command;
|
||||
|
||||
pub struct LemmyContext {
|
||||
pub pool: DbPool,
|
||||
pub chat_server: Addr<ChatServer>,
|
||||
pub client: Client,
|
||||
pub activity_queue: QueueHandle,
|
||||
}
|
||||
|
||||
impl LemmyContext {
|
||||
pub fn new(
|
||||
pool: DbPool,
|
||||
chat_server: Addr<ChatServer>,
|
||||
client: Client,
|
||||
activity_queue: QueueHandle,
|
||||
) -> LemmyContext {
|
||||
LemmyContext {
|
||||
pool,
|
||||
chat_server,
|
||||
client,
|
||||
activity_queue,
|
||||
}
|
||||
}
|
||||
pub fn pool(&self) -> &DbPool {
|
||||
&self.pool
|
||||
}
|
||||
pub fn chat_server(&self) -> &Addr<ChatServer> {
|
||||
&self.chat_server
|
||||
}
|
||||
pub fn client(&self) -> &Client {
|
||||
&self.client
|
||||
}
|
||||
pub fn activity_queue(&self) -> &QueueHandle {
|
||||
&self.activity_queue
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for LemmyContext {
|
||||
fn clone(&self) -> Self {
|
||||
LemmyContext::new(
|
||||
self.pool.clone(),
|
||||
self.chat_server.clone(),
|
||||
self.client.clone(),
|
||||
self.activity_queue.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct IframelyResponse {
|
||||
title: Option<String>,
|
||||
description: Option<String>,
|
||||
thumbnail_url: Option<String>,
|
||||
html: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn fetch_iframely(client: &Client, url: &str) -> Result<IframelyResponse, LemmyError> {
|
||||
let fetch_url = format!("http://iframely/oembed?url={}", url);
|
||||
|
||||
let response = retry(|| client.get(&fetch_url).send()).await?;
|
||||
|
||||
let res: IframelyResponse = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| RecvError(e.to_string()))?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct PictrsResponse {
|
||||
files: Vec<PictrsFile>,
|
||||
msg: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct PictrsFile {
|
||||
file: String,
|
||||
delete_token: String,
|
||||
}
|
||||
|
||||
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 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(anyhow!("{}", &response.msg).into())
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_iframely_and_pictrs_data(
|
||||
client: &Client,
|
||||
url: Option<String>,
|
||||
) -> (
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
) {
|
||||
match &url {
|
||||
Some(url) => {
|
||||
// Fetch iframely data
|
||||
let (iframely_title, iframely_description, iframely_thumbnail_url, iframely_html) =
|
||||
match fetch_iframely(client, url).await {
|
||||
Ok(res) => (res.title, res.description, res.thumbnail_url, res.html),
|
||||
Err(e) => {
|
||||
error!("iframely err: {}", e);
|
||||
(None, None, None, None)
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch pictrs thumbnail
|
||||
let pictrs_hash = match 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);
|
||||
None
|
||||
}
|
||||
},
|
||||
// Try to generate a small thumbnail if iframely is not supported
|
||||
None => match fetch_pictrs(client, &url).await {
|
||||
Ok(res) => Some(res.files[0].file.to_owned()),
|
||||
Err(e) => {
|
||||
error!("pictrs err: {}", e);
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// The full urls are necessary for federation
|
||||
let pictrs_thumbnail = if let Some(pictrs_hash) = pictrs_hash {
|
||||
Some(format!(
|
||||
"{}://{}/pictrs/image/{}",
|
||||
get_apub_protocol_string(),
|
||||
Settings::get().hostname,
|
||||
pictrs_hash
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(
|
||||
iframely_title,
|
||||
iframely_description,
|
||||
iframely_html,
|
||||
pictrs_thumbnail,
|
||||
)
|
||||
}
|
||||
None => (None, None, None, None),
|
||||
}
|
||||
}
|
||||
|
||||
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(|| anyhow!("No Content-Type header"))?
|
||||
.to_str()?
|
||||
.starts_with("image/")
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("Not an image type.").into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyError> {
|
||||
let mut built_text = String::new();
|
||||
|
||||
// Building proper speech text for espeak
|
||||
for mut c in captcha.chars() {
|
||||
let new_str = if c.is_alphabetic() {
|
||||
if c.is_lowercase() {
|
||||
c.make_ascii_uppercase();
|
||||
format!("lower case {} ... ", c)
|
||||
} else {
|
||||
c.make_ascii_uppercase();
|
||||
format!("capital {} ... ", c)
|
||||
}
|
||||
} else {
|
||||
format!("{} ...", c)
|
||||
};
|
||||
|
||||
built_text.push_str(&new_str);
|
||||
}
|
||||
|
||||
espeak_wav_base64(&built_text)
|
||||
}
|
||||
|
||||
pub fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
|
||||
// Make a temp file path
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
let file_path = format!("/tmp/lemmy_espeak_{}.wav", &uuid);
|
||||
|
||||
// Write the wav file
|
||||
Command::new("espeak")
|
||||
.arg("-w")
|
||||
.arg(&file_path)
|
||||
.arg(text)
|
||||
.status()?;
|
||||
|
||||
// Read the wav file bytes
|
||||
let bytes = std::fs::read(&file_path)?;
|
||||
|
||||
// Delete the file
|
||||
std::fs::remove_file(file_path)?;
|
||||
|
||||
// Convert to base64
|
||||
let base64 = base64::encode(bytes);
|
||||
|
||||
Ok(base64)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{captcha_espeak_wav_base64, is_image_content_type};
|
||||
|
||||
#[test]
|
||||
fn test_image() {
|
||||
actix_rt::System::new("tset_image").block_on(async move {
|
||||
let client = reqwest::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]
|
||||
fn test_espeak() {
|
||||
assert!(captcha_espeak_wav_base64("WxRt2l").is_ok())
|
||||
}
|
||||
|
||||
// These helped with testing
|
||||
// #[test]
|
||||
// fn test_iframely() {
|
||||
// let res = fetch_iframely(client, "https://www.redspark.nu/?p=15341").await;
|
||||
// assert!(res.is_ok());
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_pictshare() {
|
||||
// let res = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpg");
|
||||
// assert!(res.is_ok());
|
||||
// let res_other = fetch_pictshare("https://upload.wikimedia.org/wikipedia/en/2/27/The_Mandalorian_logo.jpgaoeu");
|
||||
// assert!(res_other.is_err());
|
||||
// }
|
||||
}
|
||||
|
14
src/main.rs
14
src/main.rs
@ -16,17 +16,14 @@ use diesel::{
|
||||
PgConnection,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use lemmy_api::match_websocket_operation;
|
||||
use lemmy_apub::activity_queue::create_activity_queue;
|
||||
use lemmy_db::get_database_url_from_env;
|
||||
use lemmy_rate_limit::{rate_limiter::RateLimiter, RateLimit};
|
||||
use lemmy_server::{
|
||||
apub::activity_queue::create_activity_queue,
|
||||
code_migrations::run_advanced_migrations,
|
||||
routes::*,
|
||||
websocket::chat_server::ChatServer,
|
||||
LemmyContext,
|
||||
};
|
||||
use lemmy_server::{code_migrations::run_advanced_migrations, routes::*};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{settings::Settings, LemmyError, CACHE_CONTROL_REGEX};
|
||||
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
||||
use reqwest::Client;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
@ -77,6 +74,7 @@ async fn main() -> Result<(), LemmyError> {
|
||||
let chat_server = ChatServer::startup(
|
||||
pool.clone(),
|
||||
rate_limiter.clone(),
|
||||
|c, i, o, d| Box::pin(match_websocket_operation(c, i, o, d)),
|
||||
Client::default(),
|
||||
activity_queue.clone(),
|
||||
)
|
||||
@ -84,7 +82,7 @@ async fn main() -> Result<(), LemmyError> {
|
||||
|
||||
// Create Http server with websocket support
|
||||
HttpServer::new(move || {
|
||||
let context = LemmyContext::new(
|
||||
let context = LemmyContext::create(
|
||||
pool.clone(),
|
||||
chat_server.to_owned(),
|
||||
Client::default(),
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::{api::Perform, LemmyContext};
|
||||
use actix_web::{error::ErrorBadRequest, *};
|
||||
use lemmy_api::Perform;
|
||||
use lemmy_rate_limit::RateLimit;
|
||||
use lemmy_structs::{comment::*, community::*, post::*, site::*, user::*};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::apub::{
|
||||
use actix_web::*;
|
||||
use http_signature_normalization_actix::digest::middleware::VerifyDigest;
|
||||
use lemmy_apub::{
|
||||
comment::get_apub_comment,
|
||||
community::*,
|
||||
inbox::{community_inbox::community_inbox, shared_inbox::shared_inbox, user_inbox::user_inbox},
|
||||
@ -6,8 +8,6 @@ use crate::apub::{
|
||||
user::*,
|
||||
APUB_JSON_CONTENT_TYPE,
|
||||
};
|
||||
use actix_web::*;
|
||||
use http_signature_normalization_actix::digest::middleware::VerifyDigest;
|
||||
use lemmy_utils::settings::Settings;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{api::claims::Claims, LemmyContext};
|
||||
use actix_web::{error::ErrorBadRequest, *};
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use diesel::PgConnection;
|
||||
use lemmy_api::claims::Claims;
|
||||
use lemmy_db::{
|
||||
comment_view::{ReplyQueryBuilder, ReplyView},
|
||||
community::Community,
|
||||
@ -15,6 +15,7 @@ use lemmy_db::{
|
||||
};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{settings::Settings, utils::markdown_to_html, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder};
|
||||
use serde::Deserialize;
|
||||
use std::str::FromStr;
|
||||
|
@ -1,9 +1,10 @@
|
||||
use crate::{version, LemmyContext};
|
||||
use actix_web::{body::Body, error::ErrorBadRequest, *};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api::version;
|
||||
use lemmy_db::site_view::SiteView;
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_utils::{apub::get_apub_protocol_string, settings::Settings, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -1,38 +1,21 @@
|
||||
use crate::LemmyContext;
|
||||
use actix_web::{error::ErrorBadRequest, web::Query, *};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_db::{community::Community, user::User_};
|
||||
use lemmy_structs::blocking;
|
||||
use lemmy_structs::{blocking, WebFingerLink, WebFingerResponse};
|
||||
use lemmy_utils::{
|
||||
settings::Settings,
|
||||
LemmyError,
|
||||
WEBFINGER_COMMUNITY_REGEX,
|
||||
WEBFINGER_USER_REGEX,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Params {
|
||||
resource: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WebFingerResponse {
|
||||
pub subject: String,
|
||||
pub aliases: Vec<String>,
|
||||
pub links: Vec<WebFingerLink>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WebFingerLink {
|
||||
pub rel: Option<String>,
|
||||
#[serde(rename(serialize = "type", deserialize = "type"))]
|
||||
pub type_: Option<String>,
|
||||
pub href: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
if Settings::get().federation.enabled {
|
||||
cfg.route(
|
||||
|
@ -1,9 +1,12 @@
|
||||
use crate::{websocket::chat_server::ChatServer, LemmyContext};
|
||||
use actix::prelude::*;
|
||||
use actix_web::*;
|
||||
use actix_web_actors::ws;
|
||||
use lemmy_structs::websocket::{Connect, Disconnect, StandardMessage, WSMessage};
|
||||
use lemmy_utils::utils::get_ip;
|
||||
use lemmy_websocket::{
|
||||
chat_server::ChatServer,
|
||||
messages::{Connect, Disconnect, StandardMessage, WSMessage},
|
||||
LemmyContext,
|
||||
};
|
||||
use log::{debug, error, info};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
pub mod chat_server;
|
||||
pub mod handlers;
|
Loading…
Reference in New Issue
Block a user