Merge branch 'master' into federation
This commit is contained in:
commit
eaf548b5db
71 changed files with 2602 additions and 2676 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
ansible/inventory
|
ansible/inventory
|
||||||
ansible/passwords/
|
ansible/passwords/
|
||||||
|
docker/lemmy_mine.hjson
|
||||||
build/
|
build/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
11
.travis.yml
vendored
11
.travis.yml
vendored
|
@ -5,11 +5,14 @@ matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
cache:
|
cache: cargo
|
||||||
directories:
|
|
||||||
- /home/travis/.cargo
|
|
||||||
before_cache:
|
before_cache:
|
||||||
- rm -rf /home/travis/.cargo/registry
|
- rm -rfv target/debug/incremental/lemmy_server-*
|
||||||
|
- rm -rfv target/debug/.fingerprint/lemmy_server-*
|
||||||
|
- rm -rfv target/debug/build/lemmy_server-*
|
||||||
|
- rm -rfv target/debug/deps/lemmy_server-*
|
||||||
|
- rm -rfv target/debug/lemmy_server.d
|
||||||
|
- cargo clean
|
||||||
before_script:
|
before_script:
|
||||||
- psql -c "create user lemmy with password 'password' superuser;" -U postgres
|
- psql -c "create user lemmy with password 'password' superuser;" -U postgres
|
||||||
- psql -c 'create database lemmy with owner lemmy;' -U postgres
|
- psql -c 'create database lemmy with owner lemmy;' -U postgres
|
||||||
|
|
18
README.md
vendored
18
README.md
vendored
|
@ -149,15 +149,15 @@ If you'd like to add translations, take a look a look at the [English translatio
|
||||||
|
|
||||||
lang | done | missing
|
lang | done | missing
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
de | 96% | avatar,docs,old_password,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
de | 95% | avatar,show_avatars,docs,old_password,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
||||||
eo | 83% | number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,are_you_sure,yes,no
|
eo | 82% | number_of_communities,preview,upload_image,avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,are_you_sure,yes,no
|
||||||
es | 91% | avatar,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
es | 90% | avatar,show_avatars,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
||||||
fr | 91% | avatar,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
fr | 90% | avatar,show_avatars,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
||||||
it | 92% | avatar,archive_link,docs,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
it | 91% | avatar,show_avatars,archive_link,docs,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
||||||
nl | 85% | preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme
|
nl | 84% | preview,upload_image,avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme
|
||||||
ru | 79% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
|
ru | 78% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
|
||||||
sv | 91% | avatar,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
sv | 90% | avatar,show_avatars,archive_link,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
|
||||||
zh | 77% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
|
zh | 76% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
|
||||||
|
|
||||||
If you'd like to update this report, run:
|
If you'd like to update this report, run:
|
||||||
|
|
||||||
|
|
12
ansible/templates/nginx.conf
vendored
12
ansible/templates/nginx.conf
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
proxy_cache_path /var/cache/lemmy_frontend levels=1:2 keys_zone=lemmy_frontend_cache:10m max_size=100m use_temp_path=off;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name {{ domain }};
|
server_name {{ domain }};
|
||||||
|
@ -59,6 +61,13 @@ server {
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# Proxy Cache
|
||||||
|
proxy_cache lemmy_frontend_cache;
|
||||||
|
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
|
||||||
|
proxy_cache_revalidate on;
|
||||||
|
proxy_cache_lock on;
|
||||||
|
proxy_cache_min_uses 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /pictshare/ {
|
location /pictshare/ {
|
||||||
|
@ -68,8 +77,7 @@ server {
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
if ($request_uri ~ \.(?:ico|gif|jpe?g|png|webp|bmp|mp4)$) {
|
if ($request_uri ~ \.(?:ico|gif|jpe?g|png|webp|bmp|mp4)$) {
|
||||||
add_header Cache-Control "public";
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
expires max;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
docker/dev/Dockerfile
vendored
4
docker/dev/Dockerfile
vendored
|
@ -10,7 +10,7 @@ RUN yarn install --pure-lockfile
|
||||||
COPY ui /app/ui
|
COPY ui /app/ui
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
FROM ekidd/rust-musl-builder:1.38.0-openssl11 as rust
|
FROM ekidd/rust-musl-builder:1.40.0-openssl11 as rust
|
||||||
|
|
||||||
# Cache deps
|
# Cache deps
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
@ -33,7 +33,7 @@ RUN cargo build --frozen --release
|
||||||
# RUN cargo install diesel_cli --no-default-features --features postgres
|
# RUN cargo install diesel_cli --no-default-features --features postgres
|
||||||
|
|
||||||
|
|
||||||
FROM ekidd/rust-musl-builder:1.38.0-openssl11 as docs
|
FROM ekidd/rust-musl-builder:1.40.0-openssl11 as docs
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY docs ./docs
|
COPY docs ./docs
|
||||||
RUN sudo chown -R rust:rust .
|
RUN sudo chown -R rust:rust .
|
||||||
|
|
3
docker/dev/dev_deploy.sh
vendored
3
docker/dev/dev_deploy.sh
vendored
|
@ -7,3 +7,6 @@ git checkout dev
|
||||||
docker-compose build
|
docker-compose build
|
||||||
docker tag dev_lemmy:latest dessalines/lemmy:dev
|
docker tag dev_lemmy:latest dessalines/lemmy:dev
|
||||||
docker push dessalines/lemmy:dev
|
docker push dessalines/lemmy:dev
|
||||||
|
|
||||||
|
# SSH and pull it
|
||||||
|
ssh tyler@45.55.175.59 "cd ~/git/lemmy/docker/dev && docker pull dessalines/lemmy:dev && docker-compose up -d"
|
||||||
|
|
2
docker/prod/docker-compose.yml
vendored
2
docker/prod/docker-compose.yml
vendored
|
@ -11,7 +11,7 @@ services:
|
||||||
- lemmy_db:/var/lib/postgresql/data
|
- lemmy_db:/var/lib/postgresql/data
|
||||||
restart: always
|
restart: always
|
||||||
lemmy:
|
lemmy:
|
||||||
image: dessalines/lemmy:v0.5.14
|
image: dessalines/lemmy:v0.5.19
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8536:8536"
|
- "127.0.0.1:8536:8536"
|
||||||
restart: always
|
restart: always
|
||||||
|
|
1
server/.gitignore
vendored
1
server/.gitignore
vendored
|
@ -2,3 +2,4 @@
|
||||||
.env
|
.env
|
||||||
.idea
|
.idea
|
||||||
env_setup.sh
|
env_setup.sh
|
||||||
|
query_testing/*.json
|
||||||
|
|
2133
server/Cargo.lock
generated
vendored
2133
server/Cargo.lock
generated
vendored
File diff suppressed because it is too large
Load diff
23
server/Cargo.toml
vendored
23
server/Cargo.toml
vendored
|
@ -7,21 +7,22 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
diesel = { version = "1.4.2", features = ["postgres","chrono", "r2d2"] }
|
diesel = { version = "1.4.2", features = ["postgres","chrono", "r2d2"] }
|
||||||
diesel_migrations = "1.4.0"
|
diesel_migrations = "1.4.0"
|
||||||
dotenv = "0.14.1"
|
dotenv = "0.15.0"
|
||||||
bcrypt = "0.5.0"
|
bcrypt = "0.6.1"
|
||||||
activitypub = "0.1.5"
|
activitypub = "0.2.0"
|
||||||
chrono = { version = "0.4.7", features = ["serde"] }
|
chrono = { version = "0.4.7", features = ["serde"] }
|
||||||
failure = "0.1.5"
|
failure = "0.1.5"
|
||||||
serde_json = { version = "1.0.40", features = ["preserve_order"]}
|
serde_json = { version = "1.0.40", features = ["preserve_order"]}
|
||||||
serde = { version = "1.0.94", features = ["derive"] }
|
serde = { version = "1.0.94", features = ["derive"] }
|
||||||
actix = "0.8.3"
|
actix = "0.9.0"
|
||||||
actix-web = "1.0"
|
actix-web = "2.0.0"
|
||||||
actix-files = "0.1.3"
|
actix-files = "0.2.1"
|
||||||
actix-web-actors = "1.0"
|
actix-web-actors = "2.0.0"
|
||||||
env_logger = "0.6.2"
|
actix-rt = "1.0.0"
|
||||||
|
env_logger = "0.7.1"
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
strum = "0.15.0"
|
strum = "0.17.1"
|
||||||
strum_macros = "0.15.0"
|
strum_macros = "0.17.1"
|
||||||
jsonwebtoken = "6.0.1"
|
jsonwebtoken = "6.0.1"
|
||||||
regex = "1.1.9"
|
regex = "1.1.9"
|
||||||
lazy_static = "1.3.0"
|
lazy_static = "1.3.0"
|
||||||
|
@ -30,6 +31,6 @@ lettre_email = "0.9.2"
|
||||||
sha2 = "0.8.0"
|
sha2 = "0.8.0"
|
||||||
rss = "1.8.0"
|
rss = "1.8.0"
|
||||||
htmlescape = "0.3.1"
|
htmlescape = "0.3.1"
|
||||||
config = "0.9.3"
|
config = "0.10.1"
|
||||||
hjson = "0.8.2"
|
hjson = "0.8.2"
|
||||||
reqwest = "0.9.24"
|
reqwest = "0.9.24"
|
7
server/clean.sh
vendored
Executable file
7
server/clean.sh
vendored
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cargo update
|
||||||
|
cargo fmt
|
||||||
|
cargo check
|
||||||
|
cargo clippy
|
||||||
|
cargo outdated -R
|
20
server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/down.sql
vendored
Normal file
20
server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/down.sql
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
-- Drop the columns
|
||||||
|
drop view user_view;
|
||||||
|
alter table user_ drop column show_avatars;
|
||||||
|
alter table user_ drop column send_notifications_to_email;
|
||||||
|
|
||||||
|
-- Rebuild the view
|
||||||
|
create view user_view as
|
||||||
|
select id,
|
||||||
|
name,
|
||||||
|
avatar,
|
||||||
|
email,
|
||||||
|
fedi_name,
|
||||||
|
admin,
|
||||||
|
banned,
|
||||||
|
published,
|
||||||
|
(select count(*) from post p where p.creator_id = u.id) as number_of_posts,
|
||||||
|
(select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score,
|
||||||
|
(select count(*) from comment c where c.creator_id = u.id) as number_of_comments,
|
||||||
|
(select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score
|
||||||
|
from user_ u;
|
22
server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/up.sql
vendored
Normal file
22
server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/up.sql
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
-- Add columns
|
||||||
|
alter table user_ add column show_avatars boolean default true not null;
|
||||||
|
alter table user_ add column send_notifications_to_email boolean default false not null;
|
||||||
|
|
||||||
|
-- Rebuild the user_view
|
||||||
|
drop view user_view;
|
||||||
|
create view user_view as
|
||||||
|
select id,
|
||||||
|
name,
|
||||||
|
avatar,
|
||||||
|
email,
|
||||||
|
fedi_name,
|
||||||
|
admin,
|
||||||
|
banned,
|
||||||
|
show_avatars,
|
||||||
|
send_notifications_to_email,
|
||||||
|
published,
|
||||||
|
(select count(*) from post p where p.creator_id = u.id) as number_of_posts,
|
||||||
|
(select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score,
|
||||||
|
(select count(*) from comment c where c.creator_id = u.id) as number_of_comments,
|
||||||
|
(select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score
|
||||||
|
from user_ u;
|
16
server/migrations/2020-01-11-012452_add_indexes/down.sql
vendored
Normal file
16
server/migrations/2020-01-11-012452_add_indexes/down.sql
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
drop index idx_post_creator;
|
||||||
|
drop index idx_post_community;
|
||||||
|
|
||||||
|
drop index idx_post_like_post;
|
||||||
|
drop index idx_post_like_user;
|
||||||
|
|
||||||
|
drop index idx_comment_creator;
|
||||||
|
drop index idx_comment_parent;
|
||||||
|
drop index idx_comment_post;
|
||||||
|
|
||||||
|
drop index idx_comment_like_comment;
|
||||||
|
drop index idx_comment_like_user;
|
||||||
|
drop index idx_comment_like_post;
|
||||||
|
|
||||||
|
drop index idx_community_creator;
|
||||||
|
drop index idx_community_category;
|
17
server/migrations/2020-01-11-012452_add_indexes/up.sql
vendored
Normal file
17
server/migrations/2020-01-11-012452_add_indexes/up.sql
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
-- Go through all the tables joins, optimize every view, CTE, etc.
|
||||||
|
create index idx_post_creator on post (creator_id);
|
||||||
|
create index idx_post_community on post (community_id);
|
||||||
|
|
||||||
|
create index idx_post_like_post on post_like (post_id);
|
||||||
|
create index idx_post_like_user on post_like (user_id);
|
||||||
|
|
||||||
|
create index idx_comment_creator on comment (creator_id);
|
||||||
|
create index idx_comment_parent on comment (parent_id);
|
||||||
|
create index idx_comment_post on comment (post_id);
|
||||||
|
|
||||||
|
create index idx_comment_like_comment on comment_like (comment_id);
|
||||||
|
create index idx_comment_like_user on comment_like (user_id);
|
||||||
|
create index idx_comment_like_post on comment_like (post_id);
|
||||||
|
|
||||||
|
create index idx_community_creator on community (creator_id);
|
||||||
|
create index idx_community_category on community (category_id);
|
24
server/query_testing/apache_bench_report.sh
vendored
Executable file
24
server/query_testing/apache_bench_report.sh
vendored
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
declare -a arr=(
|
||||||
|
"https://mastodon.social/"
|
||||||
|
"https://peertube.social/"
|
||||||
|
"https://dev.lemmy.ml/"
|
||||||
|
"https://dev.lemmy.ml/feeds/all.xml"
|
||||||
|
"https://dev.lemmy.ml/.well-known/nodeinfo"
|
||||||
|
"https://fediverse.blog/.well-known/nodeinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
## now loop through the above array
|
||||||
|
for i in "${arr[@]}"
|
||||||
|
do
|
||||||
|
ab -c 10 -t 10 "$i" > out.abtest
|
||||||
|
grep "Server Hostname:" out.abtest
|
||||||
|
grep "Document Path:" out.abtest
|
||||||
|
grep "Requests per second" out.abtest
|
||||||
|
grep "(mean, across all concurrent requests)" out.abtest
|
||||||
|
grep "Transfer rate:" out.abtest
|
||||||
|
echo "---"
|
||||||
|
done
|
||||||
|
|
||||||
|
rm *.abtest
|
25
server/query_testing/generate_explain_reports.sh
vendored
Executable file
25
server/query_testing/generate_explain_reports.sh
vendored
Executable file
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Do the views first
|
||||||
|
|
||||||
|
echo "explain (analyze, format json) select * from user_view" > explain.sql
|
||||||
|
psql -qAt -U lemmy -f explain.sql > user_view.json
|
||||||
|
|
||||||
|
echo "explain (analyze, format json) select * from post_view where user_id is null order by hot_rank desc" > explain.sql
|
||||||
|
psql -qAt -U lemmy -f explain.sql > post_view.json
|
||||||
|
|
||||||
|
echo "explain (analyze, format json) select * from post" > explain.sql
|
||||||
|
psql -qAt -U lemmy -f explain.sql > post.json
|
||||||
|
|
||||||
|
echo "explain (analyze, format json) select * from comment_view where user_id is null" > explain.sql
|
||||||
|
psql -qAt -U lemmy -f explain.sql > comment_view.json
|
||||||
|
|
||||||
|
echo "explain (analyze, format json) select * from community_view where user_id is null order by hot_rank desc" > explain.sql
|
||||||
|
psql -qAt -U lemmy -f explain.sql > community_view.json
|
||||||
|
|
||||||
|
echo "explain (analyze, format json) select * from site_view limit 1" > explain.sql
|
||||||
|
psql -qAt -U lemmy -f explain.sql > site_view.json
|
||||||
|
|
||||||
|
grep "Execution Time" *.json
|
||||||
|
|
||||||
|
rm explain.sql
|
|
@ -1,4 +1,7 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::send_email;
|
||||||
|
use crate::settings::Settings;
|
||||||
|
use diesel::PgConnection;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CreateComment {
|
pub struct CreateComment {
|
||||||
|
@ -45,9 +48,8 @@ pub struct CreateCommentLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CommentResponse> for Oper<CreateComment> {
|
impl Perform<CommentResponse> for Oper<CreateComment> {
|
||||||
fn perform(&self) -> Result<CommentResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
|
||||||
let data: &CreateComment = &self.data;
|
let data: &CreateComment = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -56,6 +58,8 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
let hostname = &format!("https://{}", Settings::get().hostname);
|
||||||
|
|
||||||
// Check for a community ban
|
// Check for a community ban
|
||||||
let post = Post::read(&conn, data.post_id)?;
|
let post = Post::read(&conn, data.post_id)?;
|
||||||
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
|
||||||
|
@ -89,17 +93,13 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
||||||
let extracted_usernames = extract_usernames(&comment_form.content);
|
let extracted_usernames = extract_usernames(&comment_form.content);
|
||||||
|
|
||||||
for username_mention in &extracted_usernames {
|
for username_mention in &extracted_usernames {
|
||||||
let mention_user = User_::read_from_name(&conn, (*username_mention).to_string());
|
if let Ok(mention_user) = User_::read_from_name(&conn, (*username_mention).to_string()) {
|
||||||
|
|
||||||
if mention_user.is_ok() {
|
|
||||||
let mention_user_id = mention_user?.id;
|
|
||||||
|
|
||||||
// You can't mention yourself
|
// You can't mention yourself
|
||||||
// At some point, make it so you can't tag the parent creator either
|
// At some point, make it so you can't tag the parent creator either
|
||||||
// This can cause two notifications, one for reply and the other for mention
|
// This can cause two notifications, one for reply and the other for mention
|
||||||
if mention_user_id != user_id {
|
if mention_user.id != user_id {
|
||||||
let user_mention_form = UserMentionForm {
|
let user_mention_form = UserMentionForm {
|
||||||
recipient_id: mention_user_id,
|
recipient_id: mention_user.id,
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
read: None,
|
read: None,
|
||||||
};
|
};
|
||||||
|
@ -109,10 +109,79 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
||||||
match UserMention::create(&conn, &user_mention_form) {
|
match UserMention::create(&conn, &user_mention_form) {
|
||||||
Ok(_mention) => (),
|
Ok(_mention) => (),
|
||||||
Err(_e) => eprintln!("{}", &_e),
|
Err(_e) => eprintln!("{}", &_e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send an email to those users that have notifications on
|
||||||
|
if mention_user.send_notifications_to_email {
|
||||||
|
if let Some(mention_email) = mention_user.email {
|
||||||
|
let subject = &format!(
|
||||||
|
"{} - Mentioned by {}",
|
||||||
|
Settings::get().hostname,
|
||||||
|
claims.username
|
||||||
|
);
|
||||||
|
let html = &format!(
|
||||||
|
"<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
|
||||||
|
claims.username, comment_form.content, hostname
|
||||||
|
);
|
||||||
|
match send_email(subject, &mention_email, &mention_user.name, html) {
|
||||||
|
Ok(_o) => _o,
|
||||||
|
Err(e) => eprintln!("{}", e),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send notifs to the parent commenter / poster
|
||||||
|
match data.parent_id {
|
||||||
|
Some(parent_id) => {
|
||||||
|
let parent_comment = Comment::read(&conn, parent_id)?;
|
||||||
|
if parent_comment.creator_id != user_id {
|
||||||
|
let parent_user = User_::read(&conn, parent_comment.creator_id)?;
|
||||||
|
if parent_user.send_notifications_to_email {
|
||||||
|
if let Some(comment_reply_email) = parent_user.email {
|
||||||
|
let subject = &format!(
|
||||||
|
"{} - Reply from {}",
|
||||||
|
Settings::get().hostname,
|
||||||
|
claims.username
|
||||||
|
);
|
||||||
|
let html = &format!(
|
||||||
|
"<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
|
||||||
|
claims.username, comment_form.content, hostname
|
||||||
|
);
|
||||||
|
match send_email(subject, &comment_reply_email, &parent_user.name, html) {
|
||||||
|
Ok(_o) => _o,
|
||||||
|
Err(e) => eprintln!("{}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Its a post
|
||||||
|
None => {
|
||||||
|
if post.creator_id != user_id {
|
||||||
|
let parent_user = User_::read(&conn, post.creator_id)?;
|
||||||
|
if parent_user.send_notifications_to_email {
|
||||||
|
if let Some(post_reply_email) = parent_user.email {
|
||||||
|
let subject = &format!(
|
||||||
|
"{} - Reply from {}",
|
||||||
|
Settings::get().hostname,
|
||||||
|
claims.username
|
||||||
|
);
|
||||||
|
let html = &format!(
|
||||||
|
"<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
|
||||||
|
claims.username, comment_form.content, hostname
|
||||||
|
);
|
||||||
|
match send_email(subject, &post_reply_email, &parent_user.name, html) {
|
||||||
|
Ok(_o) => _o,
|
||||||
|
Err(e) => eprintln!("{}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// You like your own comment by default
|
// You like your own comment by default
|
||||||
let like_form = CommentLikeForm {
|
let like_form = CommentLikeForm {
|
||||||
|
@ -137,9 +206,8 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CommentResponse> for Oper<EditComment> {
|
impl Perform<CommentResponse> for Oper<EditComment> {
|
||||||
fn perform(&self) -> Result<CommentResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
|
||||||
let data: &EditComment = &self.data;
|
let data: &EditComment = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -249,9 +317,8 @@ impl Perform<CommentResponse> for Oper<EditComment> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CommentResponse> for Oper<SaveComment> {
|
impl Perform<CommentResponse> for Oper<SaveComment> {
|
||||||
fn perform(&self) -> Result<CommentResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
|
||||||
let data: &SaveComment = &self.data;
|
let data: &SaveComment = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -287,9 +354,8 @@ impl Perform<CommentResponse> for Oper<SaveComment> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CommentResponse> for Oper<CreateCommentLike> {
|
impl Perform<CommentResponse> for Oper<CreateCommentLike> {
|
||||||
fn perform(&self) -> Result<CommentResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
|
||||||
let data: &CreateCommentLike = &self.data;
|
let data: &CreateCommentLike = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use diesel::PgConnection;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -118,9 +119,8 @@ pub struct TransferCommunity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
|
impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
|
||||||
fn perform(&self) -> Result<GetCommunityResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
|
||||||
let data: &GetCommunity = &self.data;
|
let data: &GetCommunity = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user_id: Option<i32> = match &data.auth {
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
Some(auth) => match Claims::decode(&auth) {
|
||||||
|
@ -173,9 +173,8 @@ impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CommunityResponse> for Oper<CreateCommunity> {
|
impl Perform<CommunityResponse> for Oper<CreateCommunity> {
|
||||||
fn perform(&self) -> Result<CommunityResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
|
||||||
let data: &CreateCommunity = &self.data;
|
let data: &CreateCommunity = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -248,15 +247,13 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CommunityResponse> for Oper<EditCommunity> {
|
impl Perform<CommunityResponse> for Oper<EditCommunity> {
|
||||||
fn perform(&self) -> Result<CommunityResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
|
||||||
let data: &EditCommunity = &self.data;
|
let data: &EditCommunity = &self.data;
|
||||||
|
|
||||||
if has_slurs(&data.name) || has_slurs(&data.title) {
|
if has_slurs(&data.name) || has_slurs(&data.title) {
|
||||||
return Err(APIError::err(&self.op, "no_slurs").into());
|
return Err(APIError::err(&self.op, "no_slurs").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
|
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
|
||||||
|
@ -325,9 +322,8 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
|
impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
|
||||||
fn perform(&self) -> Result<ListCommunitiesResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<ListCommunitiesResponse, Error> {
|
||||||
let data: &ListCommunities = &self.data;
|
let data: &ListCommunities = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let user_claims: Option<Claims> = match &data.auth {
|
let user_claims: Option<Claims> = match &data.auth {
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
Some(auth) => match Claims::decode(&auth) {
|
||||||
|
@ -366,9 +362,8 @@ impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CommunityResponse> for Oper<FollowCommunity> {
|
impl Perform<CommunityResponse> for Oper<FollowCommunity> {
|
||||||
fn perform(&self) -> Result<CommunityResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
|
||||||
let data: &FollowCommunity = &self.data;
|
let data: &FollowCommunity = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -404,9 +399,8 @@ impl Perform<CommunityResponse> for Oper<FollowCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
|
impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
|
||||||
fn perform(&self) -> Result<GetFollowedCommunitiesResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetFollowedCommunitiesResponse, Error> {
|
||||||
let data: &GetFollowedCommunities = &self.data;
|
let data: &GetFollowedCommunities = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -430,9 +424,8 @@ impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
|
impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
|
||||||
fn perform(&self) -> Result<BanFromCommunityResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<BanFromCommunityResponse, Error> {
|
||||||
let data: &BanFromCommunity = &self.data;
|
let data: &BanFromCommunity = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -485,9 +478,8 @@ impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
|
impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
|
||||||
fn perform(&self) -> Result<AddModToCommunityResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<AddModToCommunityResponse, Error> {
|
||||||
let data: &AddModToCommunity = &self.data;
|
let data: &AddModToCommunity = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -536,9 +528,8 @@ impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
|
impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
|
||||||
fn perform(&self) -> Result<GetCommunityResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
|
||||||
let data: &TransferCommunity = &self.data;
|
let data: &TransferCommunity = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::db::user_mention_view::*;
|
||||||
use crate::db::user_view::*;
|
use crate::db::user_view::*;
|
||||||
use crate::db::*;
|
use crate::db::*;
|
||||||
use crate::{extract_usernames, has_slurs, naive_from_unix, naive_now, remove_slurs};
|
use crate::{extract_usernames, has_slurs, naive_from_unix, naive_now, remove_slurs};
|
||||||
|
use diesel::PgConnection;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -96,7 +97,7 @@ impl<T> Oper<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Perform<T> {
|
pub trait Perform<T> {
|
||||||
fn perform(&self) -> Result<T, Error>
|
fn perform(&self, conn: &PgConnection) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
T: Sized;
|
T: Sized;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use diesel::PgConnection;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -87,9 +88,8 @@ pub struct SavePost {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<PostResponse> for Oper<CreatePost> {
|
impl Perform<PostResponse> for Oper<CreatePost> {
|
||||||
fn perform(&self) -> Result<PostResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
|
||||||
let data: &CreatePost = &self.data;
|
let data: &CreatePost = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -158,9 +158,8 @@ impl Perform<PostResponse> for Oper<CreatePost> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetPostResponse> for Oper<GetPost> {
|
impl Perform<GetPostResponse> for Oper<GetPost> {
|
||||||
fn perform(&self) -> Result<GetPostResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetPostResponse, Error> {
|
||||||
let data: &GetPost = &self.data;
|
let data: &GetPost = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user_id: Option<i32> = match &data.auth {
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
Some(auth) => match Claims::decode(&auth) {
|
||||||
|
@ -207,9 +206,8 @@ impl Perform<GetPostResponse> for Oper<GetPost> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetPostsResponse> for Oper<GetPosts> {
|
impl Perform<GetPostsResponse> for Oper<GetPosts> {
|
||||||
fn perform(&self) -> Result<GetPostsResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetPostsResponse, Error> {
|
||||||
let data: &GetPosts = &self.data;
|
let data: &GetPosts = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let user_claims: Option<Claims> = match &data.auth {
|
let user_claims: Option<Claims> = match &data.auth {
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
Some(auth) => match Claims::decode(&auth) {
|
||||||
|
@ -254,9 +252,8 @@ impl Perform<GetPostsResponse> for Oper<GetPosts> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
|
impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
|
||||||
fn perform(&self) -> Result<CreatePostLikeResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<CreatePostLikeResponse, Error> {
|
||||||
let data: &CreatePostLike = &self.data;
|
let data: &CreatePostLike = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -316,14 +313,12 @@ impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<PostResponse> for Oper<EditPost> {
|
impl Perform<PostResponse> for Oper<EditPost> {
|
||||||
fn perform(&self) -> Result<PostResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
|
||||||
let data: &EditPost = &self.data;
|
let data: &EditPost = &self.data;
|
||||||
if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
|
if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
|
||||||
return Err(APIError::err(&self.op, "no_slurs").into());
|
return Err(APIError::err(&self.op, "no_slurs").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
|
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
|
||||||
|
@ -412,9 +407,8 @@ impl Perform<PostResponse> for Oper<EditPost> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<PostResponse> for Oper<SavePost> {
|
impl Perform<PostResponse> for Oper<SavePost> {
|
||||||
fn perform(&self) -> Result<PostResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
|
||||||
let data: &SavePost = &self.data;
|
let data: &SavePost = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use diesel::PgConnection;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -97,9 +98,8 @@ pub struct TransferSite {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
|
impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
|
||||||
fn perform(&self) -> Result<ListCategoriesResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<ListCategoriesResponse, Error> {
|
||||||
let _data: &ListCategories = &self.data;
|
let _data: &ListCategories = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let categories: Vec<Category> = Category::list_all(&conn)?;
|
let categories: Vec<Category> = Category::list_all(&conn)?;
|
||||||
|
|
||||||
|
@ -112,9 +112,8 @@ impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetModlogResponse> for Oper<GetModlog> {
|
impl Perform<GetModlogResponse> for Oper<GetModlog> {
|
||||||
fn perform(&self) -> Result<GetModlogResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetModlogResponse, Error> {
|
||||||
let data: &GetModlog = &self.data;
|
let data: &GetModlog = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let removed_posts = ModRemovePostView::list(
|
let removed_posts = ModRemovePostView::list(
|
||||||
&conn,
|
&conn,
|
||||||
|
@ -187,9 +186,8 @@ impl Perform<GetModlogResponse> for Oper<GetModlog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<SiteResponse> for Oper<CreateSite> {
|
impl Perform<SiteResponse> for Oper<CreateSite> {
|
||||||
fn perform(&self) -> Result<SiteResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
|
||||||
let data: &CreateSite = &self.data;
|
let data: &CreateSite = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -234,9 +232,8 @@ impl Perform<SiteResponse> for Oper<CreateSite> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<SiteResponse> for Oper<EditSite> {
|
impl Perform<SiteResponse> for Oper<EditSite> {
|
||||||
fn perform(&self) -> Result<SiteResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
|
||||||
let data: &EditSite = &self.data;
|
let data: &EditSite = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -283,9 +280,8 @@ impl Perform<SiteResponse> for Oper<EditSite> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetSiteResponse> for Oper<GetSite> {
|
impl Perform<GetSiteResponse> for Oper<GetSite> {
|
||||||
fn perform(&self) -> Result<GetSiteResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
|
||||||
let _data: &GetSite = &self.data;
|
let _data: &GetSite = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
// It can return a null site in order to redirect
|
// It can return a null site in order to redirect
|
||||||
let site_view = match Site::read(&conn, 1) {
|
let site_view = match Site::read(&conn, 1) {
|
||||||
|
@ -314,9 +310,8 @@ impl Perform<GetSiteResponse> for Oper<GetSite> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<SearchResponse> for Oper<Search> {
|
impl Perform<SearchResponse> for Oper<Search> {
|
||||||
fn perform(&self) -> Result<SearchResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<SearchResponse, Error> {
|
||||||
let data: &Search = &self.data;
|
let data: &Search = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let sort = SortType::from_str(&data.sort)?;
|
let sort = SortType::from_str(&data.sort)?;
|
||||||
let type_ = SearchType::from_str(&data.type_)?;
|
let type_ = SearchType::from_str(&data.type_)?;
|
||||||
|
@ -419,9 +414,8 @@ impl Perform<SearchResponse> for Oper<Search> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetSiteResponse> for Oper<TransferSite> {
|
impl Perform<GetSiteResponse> for Oper<TransferSite> {
|
||||||
fn perform(&self) -> Result<GetSiteResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
|
||||||
let data: &TransferSite = &self.data;
|
let data: &TransferSite = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::*;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::{generate_random_string, send_email};
|
use crate::{generate_random_string, send_email};
|
||||||
use bcrypt::verify;
|
use bcrypt::verify;
|
||||||
|
use diesel::PgConnection;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
@ -32,6 +33,8 @@ pub struct SaveUserSettings {
|
||||||
new_password: Option<String>,
|
new_password: Option<String>,
|
||||||
new_password_verify: Option<String>,
|
new_password_verify: Option<String>,
|
||||||
old_password: Option<String>,
|
old_password: Option<String>,
|
||||||
|
show_avatars: bool,
|
||||||
|
send_notifications_to_email: bool,
|
||||||
auth: String,
|
auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,9 +168,8 @@ pub struct PasswordChange {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<LoginResponse> for Oper<Login> {
|
impl Perform<LoginResponse> for Oper<Login> {
|
||||||
fn perform(&self) -> Result<LoginResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
|
||||||
let data: &Login = &self.data;
|
let data: &Login = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
// Fetch that username / email
|
// Fetch that username / email
|
||||||
let user: User_ = match User_::find_by_email_or_username(&conn, &data.username_or_email) {
|
let user: User_ = match User_::find_by_email_or_username(&conn, &data.username_or_email) {
|
||||||
|
@ -190,9 +192,8 @@ impl Perform<LoginResponse> for Oper<Login> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<LoginResponse> for Oper<Register> {
|
impl Perform<LoginResponse> for Oper<Register> {
|
||||||
fn perform(&self) -> Result<LoginResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
|
||||||
let data: &Register = &self.data;
|
let data: &Register = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
// Make sure site has open registration
|
// Make sure site has open registration
|
||||||
if let Ok(site) = SiteView::read(&conn) {
|
if let Ok(site) = SiteView::read(&conn) {
|
||||||
|
@ -231,6 +232,8 @@ impl Perform<LoginResponse> for Oper<Register> {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the user
|
// Create the user
|
||||||
|
@ -295,9 +298,8 @@ impl Perform<LoginResponse> for Oper<Register> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<LoginResponse> for Oper<SaveUserSettings> {
|
impl Perform<LoginResponse> for Oper<SaveUserSettings> {
|
||||||
fn perform(&self) -> Result<LoginResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
|
||||||
let data: &SaveUserSettings = &self.data;
|
let data: &SaveUserSettings = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -356,6 +358,8 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
|
||||||
default_sort_type: data.default_sort_type,
|
default_sort_type: data.default_sort_type,
|
||||||
default_listing_type: data.default_listing_type,
|
default_listing_type: data.default_listing_type,
|
||||||
lang: data.lang.to_owned(),
|
lang: data.lang.to_owned(),
|
||||||
|
show_avatars: data.show_avatars,
|
||||||
|
send_notifications_to_email: data.send_notifications_to_email,
|
||||||
};
|
};
|
||||||
|
|
||||||
let updated_user = match User_::update(&conn, user_id, &user_form) {
|
let updated_user = match User_::update(&conn, user_id, &user_form) {
|
||||||
|
@ -372,9 +376,8 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
|
impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
|
||||||
fn perform(&self) -> Result<GetUserDetailsResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetUserDetailsResponse, Error> {
|
||||||
let data: &GetUserDetails = &self.data;
|
let data: &GetUserDetails = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let user_claims: Option<Claims> = match &data.auth {
|
let user_claims: Option<Claims> = match &data.auth {
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
Some(auth) => match Claims::decode(&auth) {
|
||||||
|
@ -464,9 +467,8 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<AddAdminResponse> for Oper<AddAdmin> {
|
impl Perform<AddAdminResponse> for Oper<AddAdmin> {
|
||||||
fn perform(&self) -> Result<AddAdminResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<AddAdminResponse, Error> {
|
||||||
let data: &AddAdmin = &self.data;
|
let data: &AddAdmin = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -497,6 +499,8 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
|
||||||
default_sort_type: read_user.default_sort_type,
|
default_sort_type: read_user.default_sort_type,
|
||||||
default_listing_type: read_user.default_listing_type,
|
default_listing_type: read_user.default_listing_type,
|
||||||
lang: read_user.lang,
|
lang: read_user.lang,
|
||||||
|
show_avatars: read_user.show_avatars,
|
||||||
|
send_notifications_to_email: read_user.send_notifications_to_email,
|
||||||
};
|
};
|
||||||
|
|
||||||
match User_::update(&conn, data.user_id, &user_form) {
|
match User_::update(&conn, data.user_id, &user_form) {
|
||||||
|
@ -527,9 +531,8 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<BanUserResponse> for Oper<BanUser> {
|
impl Perform<BanUserResponse> for Oper<BanUser> {
|
||||||
fn perform(&self) -> Result<BanUserResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<BanUserResponse, Error> {
|
||||||
let data: &BanUser = &self.data;
|
let data: &BanUser = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -560,6 +563,8 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
|
||||||
default_sort_type: read_user.default_sort_type,
|
default_sort_type: read_user.default_sort_type,
|
||||||
default_listing_type: read_user.default_listing_type,
|
default_listing_type: read_user.default_listing_type,
|
||||||
lang: read_user.lang,
|
lang: read_user.lang,
|
||||||
|
show_avatars: read_user.show_avatars,
|
||||||
|
send_notifications_to_email: read_user.send_notifications_to_email,
|
||||||
};
|
};
|
||||||
|
|
||||||
match User_::update(&conn, data.user_id, &user_form) {
|
match User_::update(&conn, data.user_id, &user_form) {
|
||||||
|
@ -594,9 +599,8 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetRepliesResponse> for Oper<GetReplies> {
|
impl Perform<GetRepliesResponse> for Oper<GetReplies> {
|
||||||
fn perform(&self) -> Result<GetRepliesResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetRepliesResponse, Error> {
|
||||||
let data: &GetReplies = &self.data;
|
let data: &GetReplies = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -622,9 +626,8 @@ impl Perform<GetRepliesResponse> for Oper<GetReplies> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
|
impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
|
||||||
fn perform(&self) -> Result<GetUserMentionsResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetUserMentionsResponse, Error> {
|
||||||
let data: &GetUserMentions = &self.data;
|
let data: &GetUserMentions = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -650,9 +653,8 @@ impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<UserMentionResponse> for Oper<EditUserMention> {
|
impl Perform<UserMentionResponse> for Oper<EditUserMention> {
|
||||||
fn perform(&self) -> Result<UserMentionResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<UserMentionResponse, Error> {
|
||||||
let data: &EditUserMention = &self.data;
|
let data: &EditUserMention = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -685,9 +687,8 @@ impl Perform<UserMentionResponse> for Oper<EditUserMention> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
|
impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
|
||||||
fn perform(&self) -> Result<GetRepliesResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<GetRepliesResponse, Error> {
|
||||||
let data: &MarkAllAsRead = &self.data;
|
let data: &MarkAllAsRead = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -749,9 +750,8 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<LoginResponse> for Oper<DeleteAccount> {
|
impl Perform<LoginResponse> for Oper<DeleteAccount> {
|
||||||
fn perform(&self) -> Result<LoginResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
|
||||||
let data: &DeleteAccount = &self.data;
|
let data: &DeleteAccount = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let claims = match Claims::decode(&data.auth) {
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
|
@ -828,9 +828,8 @@ impl Perform<LoginResponse> for Oper<DeleteAccount> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
|
impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
|
||||||
fn perform(&self) -> Result<PasswordResetResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<PasswordResetResponse, Error> {
|
||||||
let data: &PasswordReset = &self.data;
|
let data: &PasswordReset = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
// Fetch that email
|
// Fetch that email
|
||||||
let user: User_ = match User_::find_by_email(&conn, &data.email) {
|
let user: User_ = match User_::find_by_email(&conn, &data.email) {
|
||||||
|
@ -862,9 +861,8 @@ impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform<LoginResponse> for Oper<PasswordChange> {
|
impl Perform<LoginResponse> for Oper<PasswordChange> {
|
||||||
fn perform(&self) -> Result<LoginResponse, Error> {
|
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
|
||||||
let data: &PasswordChange = &self.data;
|
let data: &PasswordChange = &self.data;
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
// Fetch the user_id from the token
|
// Fetch the user_id from the token
|
||||||
let user_id = PasswordResetRequest::read_from_token(&conn, &data.token)?.user_id;
|
let user_id = PasswordResetRequest::read_from_token(&conn, &data.token)?.user_id;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::apub::make_apub_endpoint;
|
use crate::apub::make_apub_endpoint;
|
||||||
use crate::db::community::Community;
|
use crate::db::community::Community;
|
||||||
use crate::db::community_view::CommunityFollowerView;
|
use crate::db::community_view::CommunityFollowerView;
|
||||||
use crate::db::establish_connection;
|
use crate::db::establish_unpooled_connection;
|
||||||
use crate::to_datetime_utc;
|
use crate::to_datetime_utc;
|
||||||
use activitypub::{actor::Group, collection::UnorderedCollection, context};
|
use activitypub::{actor::Group, collection::UnorderedCollection, context};
|
||||||
use actix_web::body::Body;
|
use actix_web::body::Body;
|
||||||
|
@ -62,7 +62,7 @@ impl Community {
|
||||||
collection.object_props.set_context_object(context()).ok();
|
collection.object_props.set_context_object(context()).ok();
|
||||||
collection.object_props.set_id_string(base_url).ok();
|
collection.object_props.set_id_string(base_url).ok();
|
||||||
|
|
||||||
let connection = establish_connection();
|
let connection = establish_unpooled_connection();
|
||||||
//As we are an object, we validated that the community id was valid
|
//As we are an object, we validated that the community id was valid
|
||||||
let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap();
|
let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap();
|
||||||
|
|
||||||
|
@ -84,8 +84,8 @@ pub struct CommunityQuery {
|
||||||
community_name: String,
|
community_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_apub_community(info: Path<CommunityQuery>) -> HttpResponse<Body> {
|
pub async fn get_apub_community(info: Path<CommunityQuery>) -> HttpResponse<Body> {
|
||||||
let connection = establish_connection();
|
let connection = establish_unpooled_connection();
|
||||||
|
|
||||||
if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
|
if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
@ -96,8 +96,8 @@ pub fn get_apub_community(info: Path<CommunityQuery>) -> HttpResponse<Body> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_apub_community_followers(info: Path<CommunityQuery>) -> HttpResponse<Body> {
|
pub async fn get_apub_community_followers(info: Path<CommunityQuery>) -> HttpResponse<Body> {
|
||||||
let connection = establish_connection();
|
let connection = establish_unpooled_connection();
|
||||||
|
|
||||||
if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
|
if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
|
|
@ -33,6 +33,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let person = user.as_person();
|
let person = user.as_person();
|
||||||
|
|
|
@ -19,7 +19,7 @@ fn fetch_communities_from_instance(domain: &str) -> Result<Vec<CommunityView>, E
|
||||||
// TODO: see if there is any standard for discovering remote actors, so we dont have to rely on lemmy apis
|
// TODO: see if there is any standard for discovering remote actors, so we dont have to rely on lemmy apis
|
||||||
let communities_uri = format!("http://{}/api/v1/communities/list?sort=Hot", domain);
|
let communities_uri = format!("http://{}/api/v1/communities/list?sort=Hot", domain);
|
||||||
let communities1: ListCommunitiesResponse = reqwest::get(&communities_uri)?.json()?;
|
let communities1: ListCommunitiesResponse = reqwest::get(&communities_uri)?.json()?;
|
||||||
let mut communities2 = communities1.communities.to_owned();
|
let mut communities2 = communities1.communities;
|
||||||
for c in &mut communities2 {
|
for c in &mut communities2 {
|
||||||
c.name = format_community_name(&c.name, domain);
|
c.name = format_community_name(&c.name, domain);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ pub fn get_remote_community_posts(name: String) -> Result<GetPosts, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse, Error> {
|
pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse, Error> {
|
||||||
let x: Vec<&str> = identifier.split("@").collect();
|
let x: Vec<&str> = identifier.split('@').collect();
|
||||||
let name = x[0].replace("!", "");
|
let name = x[0].replace("!", "");
|
||||||
let instance = x[1];
|
let instance = x[1];
|
||||||
let community_uri = format!("http://{}/federation/c/{}", instance, name);
|
let community_uri = format!("http://{}/federation/c/{}", instance, name);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::apub::make_apub_endpoint;
|
use crate::apub::make_apub_endpoint;
|
||||||
use crate::db::establish_connection;
|
use crate::db::establish_unpooled_connection;
|
||||||
use crate::db::user::User_;
|
use crate::db::user::User_;
|
||||||
use crate::to_datetime_utc;
|
use crate::to_datetime_utc;
|
||||||
use activitypub::{actor::Person, context};
|
use activitypub::{actor::Person, context};
|
||||||
|
@ -61,8 +61,8 @@ pub struct UserQuery {
|
||||||
user_name: String,
|
user_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_apub_user(info: Path<UserQuery>) -> HttpResponse<Body> {
|
pub async fn get_apub_user(info: Path<UserQuery>) -> HttpResponse<Body> {
|
||||||
let connection = establish_connection();
|
let connection = establish_unpooled_connection();
|
||||||
|
|
||||||
if let Ok(user) = User_::find_by_email_or_username(&connection, &info.user_name) {
|
if let Ok(user) = User_::find_by_email_or_username(&connection, &info.user_name) {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
|
|
@ -52,7 +52,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let categories = Category::list_all(&conn).unwrap();
|
let categories = Category::list_all(&conn).unwrap();
|
||||||
let expected_first_category = Category {
|
let expected_first_category = Category {
|
||||||
|
|
|
@ -166,7 +166,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_user = UserForm {
|
let new_user = UserForm {
|
||||||
name: "terry".into(),
|
name: "terry".into(),
|
||||||
|
@ -183,6 +183,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -364,7 +364,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_user = UserForm {
|
let new_user = UserForm {
|
||||||
name: "timmy".into(),
|
name: "timmy".into(),
|
||||||
|
@ -381,6 +381,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -212,7 +212,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_user = UserForm {
|
let new_user = UserForm {
|
||||||
name: "bobbee".into(),
|
name: "bobbee".into(),
|
||||||
|
@ -229,6 +229,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use diesel::dsl::*;
|
use diesel::dsl::*;
|
||||||
use diesel::r2d2::*;
|
|
||||||
use diesel::result::Error;
|
use diesel::result::Error;
|
||||||
use diesel::*;
|
use diesel::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -111,19 +110,9 @@ impl<T> MaybeOptional<T> for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
pub fn establish_unpooled_connection() -> PgConnection {
|
||||||
static ref PG_POOL: Pool<ConnectionManager<PgConnection>> = {
|
|
||||||
let db_url = Settings::get().get_database_url();
|
let db_url = Settings::get().get_database_url();
|
||||||
let manager = ConnectionManager::<PgConnection>::new(&db_url);
|
PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url))
|
||||||
Pool::builder()
|
|
||||||
.max_size(Settings::get().database.pool_size)
|
|
||||||
.build(manager)
|
|
||||||
.unwrap_or_else(|_| panic!("Error connecting to {}", db_url))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn establish_connection() -> PooledConnection<ConnectionManager<PgConnection>> {
|
|
||||||
PG_POOL.get().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
|
#[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -434,7 +434,7 @@ mod tests {
|
||||||
// use Crud;
|
// use Crud;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_mod = UserForm {
|
let new_mod = UserForm {
|
||||||
name: "the mod".into(),
|
name: "the mod".into(),
|
||||||
|
@ -451,6 +451,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_mod = User_::create(&conn, &new_mod).unwrap();
|
let inserted_mod = User_::create(&conn, &new_mod).unwrap();
|
||||||
|
@ -470,6 +472,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -84,7 +84,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_user = UserForm {
|
let new_user = UserForm {
|
||||||
name: "thommy prw".into(),
|
name: "thommy prw".into(),
|
||||||
|
@ -101,6 +101,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -179,7 +179,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_user = UserForm {
|
let new_user = UserForm {
|
||||||
name: "jim".into(),
|
name: "jim".into(),
|
||||||
|
@ -196,6 +196,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -290,7 +290,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let user_name = "tegan".to_string();
|
let user_name = "tegan".to_string();
|
||||||
let community_name = "test_community_3".to_string();
|
let community_name = "test_community_3".to_string();
|
||||||
|
@ -311,6 +311,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -406,7 +408,7 @@ mod tests {
|
||||||
user_id: Some(inserted_user.id),
|
user_id: Some(inserted_user.id),
|
||||||
my_vote: Some(1),
|
my_vote: Some(1),
|
||||||
id: inserted_post.id,
|
id: inserted_post.id,
|
||||||
name: post_name.to_owned(),
|
name: post_name,
|
||||||
url: None,
|
url: None,
|
||||||
body: None,
|
body: None,
|
||||||
removed: false,
|
removed: false,
|
||||||
|
@ -414,12 +416,12 @@ mod tests {
|
||||||
locked: false,
|
locked: false,
|
||||||
stickied: false,
|
stickied: false,
|
||||||
creator_id: inserted_user.id,
|
creator_id: inserted_user.id,
|
||||||
creator_name: user_name.to_owned(),
|
creator_name: user_name,
|
||||||
creator_avatar: None,
|
creator_avatar: None,
|
||||||
banned: false,
|
banned: false,
|
||||||
banned_from_community: false,
|
banned_from_community: false,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
community_name: community_name.to_owned(),
|
community_name,
|
||||||
community_removed: false,
|
community_removed: false,
|
||||||
community_deleted: false,
|
community_deleted: false,
|
||||||
community_nsfw: false,
|
community_nsfw: false,
|
||||||
|
|
|
@ -24,6 +24,8 @@ pub struct User_ {
|
||||||
pub default_sort_type: i16,
|
pub default_sort_type: i16,
|
||||||
pub default_listing_type: i16,
|
pub default_listing_type: i16,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
|
pub show_avatars: bool,
|
||||||
|
pub send_notifications_to_email: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone)]
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
@ -43,6 +45,8 @@ pub struct UserForm {
|
||||||
pub default_sort_type: i16,
|
pub default_sort_type: i16,
|
||||||
pub default_listing_type: i16,
|
pub default_listing_type: i16,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
|
pub show_avatars: bool,
|
||||||
|
pub send_notifications_to_email: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Crud<UserForm> for User_ {
|
impl Crud<UserForm> for User_ {
|
||||||
|
@ -100,6 +104,7 @@ pub struct Claims {
|
||||||
pub default_listing_type: i16,
|
pub default_listing_type: i16,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
|
pub show_avatars: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Claims {
|
impl Claims {
|
||||||
|
@ -125,6 +130,7 @@ impl User_ {
|
||||||
default_listing_type: self.default_listing_type,
|
default_listing_type: self.default_listing_type,
|
||||||
lang: self.lang.to_owned(),
|
lang: self.lang.to_owned(),
|
||||||
avatar: self.avatar.to_owned(),
|
avatar: self.avatar.to_owned(),
|
||||||
|
show_avatars: self.show_avatars.to_owned(),
|
||||||
};
|
};
|
||||||
encode(
|
encode(
|
||||||
&Header::default(),
|
&Header::default(),
|
||||||
|
@ -170,7 +176,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_user = UserForm {
|
let new_user = UserForm {
|
||||||
name: "thommy".into(),
|
name: "thommy".into(),
|
||||||
|
@ -187,6 +193,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -208,6 +216,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_user = User_::read(&conn, inserted_user.id).unwrap();
|
let read_user = User_::read(&conn, inserted_user.id).unwrap();
|
||||||
|
|
|
@ -60,7 +60,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_crud() {
|
fn test_crud() {
|
||||||
let conn = establish_connection();
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
let new_user = UserForm {
|
let new_user = UserForm {
|
||||||
name: "terrylake".into(),
|
name: "terrylake".into(),
|
||||||
|
@ -77,6 +77,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -96,6 +98,8 @@ mod tests {
|
||||||
default_sort_type: SortType::Hot as i16,
|
default_sort_type: SortType::Hot as i16,
|
||||||
default_listing_type: ListingType::Subscribed as i16,
|
default_listing_type: ListingType::Subscribed as i16,
|
||||||
lang: "browser".into(),
|
lang: "browser".into(),
|
||||||
|
show_avatars: true,
|
||||||
|
send_notifications_to_email: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_recipient = User_::create(&conn, &recipient_form).unwrap();
|
let inserted_recipient = User_::create(&conn, &recipient_form).unwrap();
|
||||||
|
|
|
@ -11,6 +11,8 @@ table! {
|
||||||
fedi_name -> Varchar,
|
fedi_name -> Varchar,
|
||||||
admin -> Bool,
|
admin -> Bool,
|
||||||
banned -> Bool,
|
banned -> Bool,
|
||||||
|
show_avatars -> Bool,
|
||||||
|
send_notifications_to_email -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
number_of_posts -> BigInt,
|
number_of_posts -> BigInt,
|
||||||
post_score -> BigInt,
|
post_score -> BigInt,
|
||||||
|
@ -31,6 +33,8 @@ pub struct UserView {
|
||||||
pub fedi_name: String,
|
pub fedi_name: String,
|
||||||
pub admin: bool,
|
pub admin: bool,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
|
pub show_avatars: bool,
|
||||||
|
pub send_notifications_to_email: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub number_of_posts: i64,
|
pub number_of_posts: i64,
|
||||||
pub post_score: i64,
|
pub post_score: i64,
|
||||||
|
|
|
@ -3,31 +3,48 @@ extern crate lemmy_server;
|
||||||
extern crate diesel_migrations;
|
extern crate diesel_migrations;
|
||||||
|
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
use lemmy_server::db::establish_connection;
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
use diesel::PgConnection;
|
||||||
use lemmy_server::routes::{federation, feeds, index, nodeinfo, webfinger, websocket};
|
use lemmy_server::routes::{federation, feeds, index, nodeinfo, webfinger, websocket};
|
||||||
use lemmy_server::settings::Settings;
|
use lemmy_server::settings::Settings;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
embed_migrations!();
|
embed_migrations!();
|
||||||
|
|
||||||
fn main() {
|
#[actix_rt::main]
|
||||||
|
async fn main() -> io::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let sys = actix::System::new("lemmy");
|
let settings = Settings::get();
|
||||||
|
|
||||||
|
// Set up the r2d2 connection pool
|
||||||
|
let manager = ConnectionManager::<PgConnection>::new(&settings.get_database_url());
|
||||||
|
let pool = Pool::builder()
|
||||||
|
.max_size(settings.database.pool_size)
|
||||||
|
.build(manager)
|
||||||
|
.unwrap_or_else(|_| panic!("Error connecting to {}", settings.get_database_url()));
|
||||||
|
|
||||||
// Run the migrations from code
|
// Run the migrations from code
|
||||||
let conn = establish_connection();
|
let conn = pool.get().unwrap();
|
||||||
embedded_migrations::run(&conn).unwrap();
|
embedded_migrations::run(&conn).unwrap();
|
||||||
|
|
||||||
let settings = Settings::get();
|
println!(
|
||||||
|
"Starting http server at {}:{}",
|
||||||
|
settings.bind, settings.port
|
||||||
|
);
|
||||||
|
|
||||||
// Create Http server with websocket support
|
// Create Http server with websocket support
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
|
.wrap(middleware::Logger::default())
|
||||||
|
.data(pool.clone())
|
||||||
|
// The routes
|
||||||
.configure(federation::config)
|
.configure(federation::config)
|
||||||
.configure(feeds::config)
|
.configure(feeds::config)
|
||||||
.configure(index::config)
|
.configure(index::config)
|
||||||
.configure(nodeinfo::config)
|
.configure(nodeinfo::config)
|
||||||
.configure(webfinger::config)
|
.configure(webfinger::config)
|
||||||
.configure(websocket::config)
|
.configure(websocket::config)
|
||||||
|
// static files
|
||||||
.service(actix_files::Files::new(
|
.service(actix_files::Files::new(
|
||||||
"/static",
|
"/static",
|
||||||
settings.front_end_dir.to_owned(),
|
settings.front_end_dir.to_owned(),
|
||||||
|
@ -37,11 +54,7 @@ fn main() {
|
||||||
settings.front_end_dir.to_owned() + "/documentation",
|
settings.front_end_dir.to_owned() + "/documentation",
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.bind((settings.bind, settings.port))
|
.bind((settings.bind, settings.port))?
|
||||||
.unwrap()
|
.run()
|
||||||
.start();
|
.await
|
||||||
|
|
||||||
println!("Started http server at {}:{}", settings.bind, settings.port);
|
|
||||||
|
|
||||||
let _ = sys.run();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ use crate::apub;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use actix_web::web::Query;
|
use actix_web::web::Query;
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
use diesel::PgConnection;
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
if Settings::get().federation_enabled {
|
if Settings::get().federation_enabled {
|
||||||
|
@ -25,14 +27,16 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
// TODO: this is a very quick and dirty implementation for http api calls
|
// TODO: this is a very quick and dirty implementation for http api calls
|
||||||
.route(
|
.route(
|
||||||
"/api/v1/communities/list",
|
"/api/v1/communities/list",
|
||||||
web::get().to(|query: Query<ListCommunities>| {
|
web::get().to(
|
||||||
|
|query: Query<ListCommunities>, db: web::Data<Pool<ConnectionManager<PgConnection>>>| {
|
||||||
let res = Oper::new(UserOperation::ListCommunities, query.into_inner())
|
let res = Oper::new(UserOperation::ListCommunities, query.into_inner())
|
||||||
.perform()
|
.perform(&db.get().unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type("application/json")
|
.content_type("application/json")
|
||||||
.body(serde_json::to_string(&res).unwrap())
|
.body(serde_json::to_string(&res).unwrap())
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,12 @@ use crate::db::post_view::{PostQueryBuilder, PostView};
|
||||||
use crate::db::site_view::SiteView;
|
use crate::db::site_view::SiteView;
|
||||||
use crate::db::user::{Claims, User_};
|
use crate::db::user::{Claims, User_};
|
||||||
use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
|
use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
|
||||||
use crate::db::{establish_connection, ListingType, SortType};
|
use crate::db::{ListingType, SortType};
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
use actix_web::body::Body;
|
|
||||||
use actix_web::{web, HttpResponse, Result};
|
use actix_web::{web, HttpResponse, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
use diesel::PgConnection;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder};
|
use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -37,51 +38,61 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
.route("/feeds/all.xml", web::get().to(feeds::get_all_feed));
|
.route("/feeds/all.xml", web::get().to(feeds::get_all_feed));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_feed(info: web::Query<Params>) -> HttpResponse<Body> {
|
async fn get_all_feed(
|
||||||
let sort_type = match get_sort_type(info) {
|
info: web::Query<Params>,
|
||||||
Ok(sort_type) => sort_type,
|
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||||
Err(_) => return HttpResponse::BadRequest().finish(),
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
};
|
let res = web::block(move || {
|
||||||
|
let conn = db.get()?;
|
||||||
|
|
||||||
let feed_result = get_feed_all_data(&sort_type);
|
let sort_type = get_sort_type(info)?;
|
||||||
|
get_feed_all_data(&conn, &sort_type)
|
||||||
match feed_result {
|
})
|
||||||
Ok(rss) => HttpResponse::Ok()
|
.await
|
||||||
|
.map(|rss| {
|
||||||
|
HttpResponse::Ok()
|
||||||
.content_type("application/rss+xml")
|
.content_type("application/rss+xml")
|
||||||
.body(rss),
|
.body(rss)
|
||||||
Err(_) => HttpResponse::NotFound().finish(),
|
})
|
||||||
}
|
.map_err(|_| HttpResponse::InternalServerError())?;
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_feed(path: web::Path<(String, String)>, info: web::Query<Params>) -> HttpResponse<Body> {
|
async fn get_feed(
|
||||||
let sort_type = match get_sort_type(info) {
|
path: web::Path<(String, String)>,
|
||||||
Ok(sort_type) => sort_type,
|
info: web::Query<Params>,
|
||||||
Err(_) => return HttpResponse::BadRequest().finish(),
|
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||||
};
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
|
let res = web::block(move || {
|
||||||
|
let conn = db.get()?;
|
||||||
|
|
||||||
|
let sort_type = get_sort_type(info)?;
|
||||||
|
|
||||||
let request_type = match path.0.as_ref() {
|
let request_type = match path.0.as_ref() {
|
||||||
"u" => RequestType::User,
|
"u" => RequestType::User,
|
||||||
"c" => RequestType::Community,
|
"c" => RequestType::Community,
|
||||||
"front" => RequestType::Front,
|
"front" => RequestType::Front,
|
||||||
"inbox" => RequestType::Inbox,
|
"inbox" => RequestType::Inbox,
|
||||||
_ => return HttpResponse::NotFound().finish(),
|
_ => return Err(format_err!("wrong_type")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let param = path.1.to_owned();
|
let param = path.1.to_owned();
|
||||||
|
|
||||||
let feed_result = match request_type {
|
match request_type {
|
||||||
RequestType::User => get_feed_user(&sort_type, param),
|
RequestType::User => get_feed_user(&conn, &sort_type, param),
|
||||||
RequestType::Community => get_feed_community(&sort_type, param),
|
RequestType::Community => get_feed_community(&conn, &sort_type, param),
|
||||||
RequestType::Front => get_feed_front(&sort_type, param),
|
RequestType::Front => get_feed_front(&conn, &sort_type, param),
|
||||||
RequestType::Inbox => get_feed_inbox(param),
|
RequestType::Inbox => get_feed_inbox(&conn, param),
|
||||||
};
|
|
||||||
|
|
||||||
match feed_result {
|
|
||||||
Ok(rss) => HttpResponse::Ok()
|
|
||||||
.content_type("application/rss+xml")
|
|
||||||
.body(rss),
|
|
||||||
Err(_) => HttpResponse::NotFound().finish(),
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(|rss| {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.content_type("application/rss+xml")
|
||||||
|
.body(rss)
|
||||||
|
})
|
||||||
|
.map_err(|_| HttpResponse::InternalServerError())?;
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
|
fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
|
||||||
|
@ -92,9 +103,7 @@ fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
|
||||||
SortType::from_str(&sort_query)
|
SortType::from_str(&sort_query)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_feed_all_data(sort_type: &SortType) -> Result<String, Error> {
|
fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result<String, failure::Error> {
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
|
|
||||||
let posts = PostQueryBuilder::create(&conn)
|
let posts = PostQueryBuilder::create(&conn)
|
||||||
|
@ -117,9 +126,11 @@ fn get_feed_all_data(sort_type: &SortType) -> Result<String, Error> {
|
||||||
Ok(channel_builder.build().unwrap().to_string())
|
Ok(channel_builder.build().unwrap().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_feed_user(sort_type: &SortType, user_name: String) -> Result<String, Error> {
|
fn get_feed_user(
|
||||||
let conn = establish_connection();
|
conn: &PgConnection,
|
||||||
|
sort_type: &SortType,
|
||||||
|
user_name: String,
|
||||||
|
) -> Result<String, Error> {
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
let user = User_::find_by_username(&conn, &user_name)?;
|
let user = User_::find_by_username(&conn, &user_name)?;
|
||||||
let user_url = user.get_profile_url();
|
let user_url = user.get_profile_url();
|
||||||
|
@ -141,9 +152,11 @@ fn get_feed_user(sort_type: &SortType, user_name: String) -> Result<String, Erro
|
||||||
Ok(channel_builder.build().unwrap().to_string())
|
Ok(channel_builder.build().unwrap().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_feed_community(sort_type: &SortType, community_name: String) -> Result<String, Error> {
|
fn get_feed_community(
|
||||||
let conn = establish_connection();
|
conn: &PgConnection,
|
||||||
|
sort_type: &SortType,
|
||||||
|
community_name: String,
|
||||||
|
) -> Result<String, Error> {
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
let community = Community::read_from_name(&conn, community_name)?;
|
let community = Community::read_from_name(&conn, community_name)?;
|
||||||
let community_url = community.get_url();
|
let community_url = community.get_url();
|
||||||
|
@ -169,9 +182,7 @@ fn get_feed_community(sort_type: &SortType, community_name: String) -> Result<St
|
||||||
Ok(channel_builder.build().unwrap().to_string())
|
Ok(channel_builder.build().unwrap().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_feed_front(sort_type: &SortType, jwt: String) -> Result<String, Error> {
|
fn get_feed_front(conn: &PgConnection, sort_type: &SortType, jwt: String) -> Result<String, Error> {
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
let user_id = Claims::decode(&jwt)?.claims.id;
|
let user_id = Claims::decode(&jwt)?.claims.id;
|
||||||
|
|
||||||
|
@ -196,9 +207,7 @@ fn get_feed_front(sort_type: &SortType, jwt: String) -> Result<String, Error> {
|
||||||
Ok(channel_builder.build().unwrap().to_string())
|
Ok(channel_builder.build().unwrap().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_feed_inbox(jwt: String) -> Result<String, Error> {
|
fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<String, Error> {
|
||||||
let conn = establish_connection();
|
|
||||||
|
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
let user_id = Claims::decode(&jwt)?.claims.id;
|
let user_id = Claims::decode(&jwt)?.claims.id;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
.route("/password_change/{token}", web::get().to(index));
|
.route("/password_change/{token}", web::get().to(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index() -> Result<NamedFile, actix_web::error::Error> {
|
async fn index() -> Result<NamedFile, actix_web::error::Error> {
|
||||||
Ok(NamedFile::open(
|
Ok(NamedFile::open(
|
||||||
Settings::get().front_end_dir.to_owned() + "/index.html",
|
Settings::get().front_end_dir.to_owned() + "/index.html",
|
||||||
)?)
|
)?)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::db::establish_connection;
|
|
||||||
use crate::db::site_view::SiteView;
|
use crate::db::site_view::SiteView;
|
||||||
use crate::version;
|
use crate::version;
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
use actix_web::body::Body;
|
use actix_web::body::Body;
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
use actix_web::HttpResponse;
|
use actix_web::HttpResponse;
|
||||||
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
use diesel::PgConnection;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
|
@ -13,7 +14,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
.route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
|
.route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_info_well_known() -> HttpResponse<Body> {
|
async fn node_info_well_known() -> HttpResponse<Body> {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"links": {
|
"links": {
|
||||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||||
|
@ -21,23 +22,24 @@ pub fn node_info_well_known() -> HttpResponse<Body> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok().json(json)
|
||||||
.content_type("application/json")
|
|
||||||
.body(json.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_info() -> HttpResponse<Body> {
|
async fn node_info(
|
||||||
let conn = establish_connection();
|
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||||
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
|
let res = web::block(move || {
|
||||||
|
let conn = db.get()?;
|
||||||
let site_view = match SiteView::read(&conn) {
|
let site_view = match SiteView::read(&conn) {
|
||||||
Ok(site_view) => site_view,
|
Ok(site_view) => site_view,
|
||||||
Err(_e) => return HttpResponse::InternalServerError().finish(),
|
Err(_) => return Err(format_err!("not_found")),
|
||||||
};
|
};
|
||||||
let protocols = if Settings::get().federation_enabled {
|
let protocols = if Settings::get().federation_enabled {
|
||||||
vec!["activitypub"]
|
vec!["activitypub"]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
let json = json!({
|
Ok(json!({
|
||||||
"version": "2.0",
|
"version": "2.0",
|
||||||
"software": {
|
"software": {
|
||||||
"name": "lemmy",
|
"name": "lemmy",
|
||||||
|
@ -52,8 +54,10 @@ fn node_info() -> HttpResponse<Body> {
|
||||||
"localComments": site_view.number_of_comments,
|
"localComments": site_view.number_of_comments,
|
||||||
"openRegistrations": site_view.open_registration,
|
"openRegistrations": site_view.open_registration,
|
||||||
}
|
}
|
||||||
});
|
}))
|
||||||
HttpResponse::Ok()
|
})
|
||||||
.content_type("application/json")
|
.await
|
||||||
.body(json.to_string())
|
.map(|json| HttpResponse::Ok().json(json))
|
||||||
|
.map_err(|_| HttpResponse::InternalServerError())?;
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::db::community::Community;
|
use crate::db::community::Community;
|
||||||
use crate::db::establish_connection;
|
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
use actix_web::body::Body;
|
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
use actix_web::web::Query;
|
use actix_web::web::Query;
|
||||||
use actix_web::HttpResponse;
|
use actix_web::HttpResponse;
|
||||||
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
use diesel::PgConnection;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -37,7 +37,13 @@ lazy_static! {
|
||||||
///
|
///
|
||||||
/// You can also view the webfinger response that Mastodon sends:
|
/// You can also view the webfinger response that Mastodon sends:
|
||||||
/// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town
|
/// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town
|
||||||
fn get_webfinger_response(info: Query<Params>) -> HttpResponse<Body> {
|
async fn get_webfinger_response(
|
||||||
|
info: Query<Params>,
|
||||||
|
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||||
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
|
let res = web::block(move || {
|
||||||
|
let conn = db.get()?;
|
||||||
|
|
||||||
let regex_parsed = WEBFINGER_COMMUNITY_REGEX
|
let regex_parsed = WEBFINGER_COMMUNITY_REGEX
|
||||||
.captures(&info.resource)
|
.captures(&info.resource)
|
||||||
.map(|c| c.get(1));
|
.map(|c| c.get(1));
|
||||||
|
@ -48,25 +54,18 @@ fn get_webfinger_response(info: Query<Params>) -> HttpResponse<Body> {
|
||||||
};
|
};
|
||||||
let community_name = match regex_parsed_flattened {
|
let community_name = match regex_parsed_flattened {
|
||||||
Some(c) => c.as_str(),
|
Some(c) => c.as_str(),
|
||||||
None => return HttpResponse::NotFound().finish(),
|
None => return Err(format_err!("not_found")),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure the requested community exists.
|
// Make sure the requested community exists.
|
||||||
let conn = establish_connection();
|
|
||||||
let community = match Community::read_from_name(&conn, community_name.to_string()) {
|
let community = match Community::read_from_name(&conn, community_name.to_string()) {
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
Err(_) => return HttpResponse::NotFound().finish(),
|
Err(_) => return Err(format_err!("not_found")),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: might want to move this into community or apub
|
let community_url = community.get_url();
|
||||||
// TODO: should use http during local testing, and https during production
|
|
||||||
let community_url = format!(
|
|
||||||
"http://{}/federation/c/{}",
|
|
||||||
Settings::get().hostname,
|
|
||||||
community.name
|
|
||||||
);
|
|
||||||
|
|
||||||
let json = json!({
|
Ok(json!({
|
||||||
"subject": info.resource,
|
"subject": info.resource,
|
||||||
"aliases": [
|
"aliases": [
|
||||||
community_url,
|
community_url,
|
||||||
|
@ -89,8 +88,10 @@ fn get_webfinger_response(info: Query<Params>) -> HttpResponse<Body> {
|
||||||
// "template": "https://my_instance.com/authorize_interaction?uri={uri}"
|
// "template": "https://my_instance.com/authorize_interaction?uri={uri}"
|
||||||
//}
|
//}
|
||||||
]
|
]
|
||||||
});
|
}))
|
||||||
HttpResponse::Ok()
|
})
|
||||||
.content_type("application/activity+json")
|
.await
|
||||||
.body(json.to_string())
|
.map(|json| HttpResponse::Ok().json(json))
|
||||||
|
.map_err(|_| HttpResponse::InternalServerError())?;
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,24 @@
|
||||||
use crate::websocket::server::*;
|
use crate::websocket::server::*;
|
||||||
|
use crate::Settings;
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web::web;
|
use actix_web::web;
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
use diesel::PgConnection;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
|
// TODO couldn't figure out how to get this method to recieve the other pool
|
||||||
|
let settings = Settings::get();
|
||||||
|
let manager = ConnectionManager::<PgConnection>::new(&settings.get_database_url());
|
||||||
|
let pool = Pool::builder()
|
||||||
|
.max_size(settings.database.pool_size)
|
||||||
|
.build(manager)
|
||||||
|
.unwrap_or_else(|_| panic!("Error connecting to {}", settings.get_database_url()));
|
||||||
|
|
||||||
// Start chat server actor in separate thread
|
// Start chat server actor in separate thread
|
||||||
let server = ChatServer::default().start();
|
let server = ChatServer::startup(pool).start();
|
||||||
cfg
|
cfg
|
||||||
.data(server)
|
.data(server)
|
||||||
.service(web::resource("/api/v1/ws").to(chat_route));
|
.service(web::resource("/api/v1/ws").to(chat_route));
|
||||||
|
@ -19,14 +30,16 @@ const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||||||
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||||
|
|
||||||
/// Entry point for our route
|
/// Entry point for our route
|
||||||
fn chat_route(
|
async fn chat_route(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
stream: web::Payload,
|
stream: web::Payload,
|
||||||
chat_server: web::Data<Addr<ChatServer>>,
|
chat_server: web::Data<Addr<ChatServer>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
|
// TODO not sure if the blocking should be here or not
|
||||||
ws::start(
|
ws::start(
|
||||||
WSSession {
|
WSSession {
|
||||||
cs_addr: chat_server.get_ref().to_owned(),
|
// db: db.get_ref().clone(),
|
||||||
|
cs_addr: chat_server.get_ref().clone(),
|
||||||
id: 0,
|
id: 0,
|
||||||
hb: Instant::now(),
|
hb: Instant::now(),
|
||||||
ip: req
|
ip: req
|
||||||
|
@ -51,6 +64,7 @@ struct WSSession {
|
||||||
/// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
|
/// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
|
||||||
/// otherwise we drop connection.
|
/// otherwise we drop connection.
|
||||||
hb: Instant,
|
hb: Instant,
|
||||||
|
// db: Pool<ConnectionManager<PgConnection>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for WSSession {
|
impl Actor for WSSession {
|
||||||
|
@ -80,7 +94,7 @@ impl Actor for WSSession {
|
||||||
// something is wrong with chat server
|
// something is wrong with chat server
|
||||||
_ => ctx.stop(),
|
_ => ctx.stop(),
|
||||||
}
|
}
|
||||||
fut::ok(())
|
actix::fut::ready(())
|
||||||
})
|
})
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
@ -107,10 +121,17 @@ impl Handler<WSMessage> for WSSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WebSocket message handler
|
/// WebSocket message handler
|
||||||
impl StreamHandler<ws::Message, ws::ProtocolError> for WSSession {
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSSession {
|
||||||
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
|
fn handle(&mut self, result: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
// println!("WEBSOCKET MESSAGE: {:?} from id: {}", msg, self.id);
|
// println!("WEBSOCKET MESSAGE: {:?} from id: {}", msg, self.id);
|
||||||
match msg {
|
let message = match result {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match message {
|
||||||
ws::Message::Ping(msg) => {
|
ws::Message::Ping(msg) => {
|
||||||
self.hb = Instant::now();
|
self.hb = Instant::now();
|
||||||
ctx.pong(&msg);
|
ctx.pong(&msg);
|
||||||
|
@ -120,7 +141,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WSSession {
|
||||||
}
|
}
|
||||||
ws::Message::Text(text) => {
|
ws::Message::Text(text) => {
|
||||||
let m = text.trim().to_owned();
|
let m = text.trim().to_owned();
|
||||||
println!("WEBSOCKET MESSAGE: {:?} from id: {}", &m, self.id);
|
// println!("WEBSOCKET MESSAGE: {:?} from id: {}", &m, self.id);
|
||||||
|
|
||||||
self
|
self
|
||||||
.cs_addr
|
.cs_addr
|
||||||
|
@ -136,7 +157,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for WSSession {
|
||||||
eprintln!("{}", &e);
|
eprintln!("{}", &e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fut::ok(())
|
actix::fut::ready(())
|
||||||
})
|
})
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
@ -173,7 +194,7 @@ impl WSSession {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.ping("");
|
ctx.ping(b"");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,6 +270,8 @@ table! {
|
||||||
default_sort_type -> Int2,
|
default_sort_type -> Int2,
|
||||||
default_listing_type -> Int2,
|
default_listing_type -> Int2,
|
||||||
lang -> Varchar,
|
lang -> Varchar,
|
||||||
|
show_avatars -> Bool,
|
||||||
|
send_notifications_to_email -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
pub const VERSION: &str = "v0.5.14";
|
pub const VERSION: &str = "v0.5.19";
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
//! room through `ChatServer`.
|
//! room through `ChatServer`.
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
use diesel::PgConnection;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use rand::{rngs::ThreadRng, Rng};
|
use rand::{rngs::ThreadRng, Rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -22,6 +24,7 @@ use crate::Settings;
|
||||||
|
|
||||||
/// Chat server sends this messages to session
|
/// Chat server sends this messages to session
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
pub struct WSMessage(pub String);
|
pub struct WSMessage(pub String);
|
||||||
|
|
||||||
/// Message for chat server communications
|
/// Message for chat server communications
|
||||||
|
@ -36,13 +39,16 @@ pub struct Connect {
|
||||||
|
|
||||||
/// Session is disconnected
|
/// Session is disconnected
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
pub struct Disconnect {
|
pub struct Disconnect {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub ip: String,
|
pub ip: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this is unused rn
|
||||||
/// Send message to specific room
|
/// Send message to specific room
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
pub struct ClientMessage {
|
pub struct ClientMessage {
|
||||||
/// Id of the client session
|
/// Id of the client session
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
|
@ -52,7 +58,8 @@ pub struct ClientMessage {
|
||||||
pub room: String,
|
pub room: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Message)]
|
||||||
|
#[rtype(String)]
|
||||||
pub struct StandardMessage {
|
pub struct StandardMessage {
|
||||||
/// Id of the client session
|
/// Id of the client session
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
|
@ -60,10 +67,6 @@ pub struct StandardMessage {
|
||||||
pub msg: String,
|
pub msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl actix::Message for StandardMessage {
|
|
||||||
type Result = String;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RateLimitBucket {
|
pub struct RateLimitBucket {
|
||||||
last_checked: SystemTime,
|
last_checked: SystemTime,
|
||||||
|
@ -82,10 +85,26 @@ pub struct ChatServer {
|
||||||
rate_limits: HashMap<String, RateLimitBucket>,
|
rate_limits: HashMap<String, RateLimitBucket>,
|
||||||
rooms: HashMap<i32, HashSet<usize>>, // A map from room / post name to set of connectionIDs
|
rooms: HashMap<i32, HashSet<usize>>, // A map from room / post name to set of connectionIDs
|
||||||
rng: ThreadRng,
|
rng: ThreadRng,
|
||||||
|
db: Pool<ConnectionManager<PgConnection>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ChatServer {
|
// impl Default for ChatServer {
|
||||||
fn default() -> ChatServer {
|
// fn default(nah: String) -> ChatServer {
|
||||||
|
// // default room
|
||||||
|
// let rooms = HashMap::new();
|
||||||
|
|
||||||
|
// ChatServer {
|
||||||
|
// sessions: HashMap::new(),
|
||||||
|
// rate_limits: HashMap::new(),
|
||||||
|
// rooms,
|
||||||
|
// rng: rand::thread_rng(),
|
||||||
|
// nah: nah,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl ChatServer {
|
||||||
|
pub fn startup(db: Pool<ConnectionManager<PgConnection>>) -> ChatServer {
|
||||||
// default room
|
// default room
|
||||||
let rooms = HashMap::new();
|
let rooms = HashMap::new();
|
||||||
|
|
||||||
|
@ -94,11 +113,10 @@ impl Default for ChatServer {
|
||||||
rate_limits: HashMap::new(),
|
rate_limits: HashMap::new(),
|
||||||
rooms,
|
rooms,
|
||||||
rng: rand::thread_rng(),
|
rng: rand::thread_rng(),
|
||||||
}
|
db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatServer {
|
|
||||||
/// Send message to all users in the room
|
/// Send message to all users in the room
|
||||||
fn send_room_message(&self, room: i32, message: &str, skip_id: usize) {
|
fn send_room_message(&self, room: i32, message: &str, skip_id: usize) {
|
||||||
if let Some(sessions) = self.rooms.get(&room) {
|
if let Some(sessions) = self.rooms.get(&room) {
|
||||||
|
@ -134,7 +152,8 @@ impl ChatServer {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
use crate::db::post_view::*;
|
use crate::db::post_view::*;
|
||||||
use crate::db::*;
|
use crate::db::*;
|
||||||
let conn = establish_connection();
|
|
||||||
|
let conn = self.db.get()?;
|
||||||
|
|
||||||
let posts = PostQueryBuilder::create(&conn)
|
let posts = PostQueryBuilder::create(&conn)
|
||||||
.listing_type(ListingType::Community)
|
.listing_type(ListingType::Community)
|
||||||
|
@ -300,17 +319,19 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
message: "Unknown op type".to_string(),
|
message: "Unknown op type".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let conn = chat.db.get()?;
|
||||||
|
|
||||||
let user_operation: UserOperation = UserOperation::from_str(&op)?;
|
let user_operation: UserOperation = UserOperation::from_str(&op)?;
|
||||||
|
|
||||||
match user_operation {
|
match user_operation {
|
||||||
UserOperation::Login => {
|
UserOperation::Login => {
|
||||||
let login: Login = serde_json::from_str(data)?;
|
let login: Login = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, login).perform()?;
|
let res = Oper::new(user_operation, login).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::Register => {
|
UserOperation::Register => {
|
||||||
let register: Register = serde_json::from_str(data)?;
|
let register: Register = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, register).perform();
|
let res = Oper::new(user_operation, register).perform(&conn);
|
||||||
if res.is_ok() {
|
if res.is_ok() {
|
||||||
chat.check_rate_limit_register(msg.id)?;
|
chat.check_rate_limit_register(msg.id)?;
|
||||||
}
|
}
|
||||||
|
@ -318,58 +339,58 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
}
|
}
|
||||||
UserOperation::GetUserDetails => {
|
UserOperation::GetUserDetails => {
|
||||||
let get_user_details: GetUserDetails = serde_json::from_str(data)?;
|
let get_user_details: GetUserDetails = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, get_user_details).perform()?;
|
let res = Oper::new(user_operation, get_user_details).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::SaveUserSettings => {
|
UserOperation::SaveUserSettings => {
|
||||||
let save_user_settings: SaveUserSettings = serde_json::from_str(data)?;
|
let save_user_settings: SaveUserSettings = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, save_user_settings).perform()?;
|
let res = Oper::new(user_operation, save_user_settings).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::AddAdmin => {
|
UserOperation::AddAdmin => {
|
||||||
let add_admin: AddAdmin = serde_json::from_str(data)?;
|
let add_admin: AddAdmin = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, add_admin).perform()?;
|
let res = Oper::new(user_operation, add_admin).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::BanUser => {
|
UserOperation::BanUser => {
|
||||||
let ban_user: BanUser = serde_json::from_str(data)?;
|
let ban_user: BanUser = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, ban_user).perform()?;
|
let res = Oper::new(user_operation, ban_user).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::GetReplies => {
|
UserOperation::GetReplies => {
|
||||||
let get_replies: GetReplies = serde_json::from_str(data)?;
|
let get_replies: GetReplies = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, get_replies).perform()?;
|
let res = Oper::new(user_operation, get_replies).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::GetUserMentions => {
|
UserOperation::GetUserMentions => {
|
||||||
let get_user_mentions: GetUserMentions = serde_json::from_str(data)?;
|
let get_user_mentions: GetUserMentions = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, get_user_mentions).perform()?;
|
let res = Oper::new(user_operation, get_user_mentions).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::EditUserMention => {
|
UserOperation::EditUserMention => {
|
||||||
let edit_user_mention: EditUserMention = serde_json::from_str(data)?;
|
let edit_user_mention: EditUserMention = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, edit_user_mention).perform()?;
|
let res = Oper::new(user_operation, edit_user_mention).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::MarkAllAsRead => {
|
UserOperation::MarkAllAsRead => {
|
||||||
let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?;
|
let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, mark_all_as_read).perform()?;
|
let res = Oper::new(user_operation, mark_all_as_read).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::GetCommunity => {
|
UserOperation::GetCommunity => {
|
||||||
let mut get_community: GetCommunity = serde_json::from_str(data)?;
|
let mut get_community: GetCommunity = serde_json::from_str(data)?;
|
||||||
if Settings::get().federation_enabled && get_community.name.is_some() {
|
if Settings::get().federation_enabled && get_community.name.is_some() {
|
||||||
let name = &get_community.name.unwrap();
|
let name = &get_community.name.unwrap();
|
||||||
let remote_community = if name.contains("@") {
|
let remote_community = if name.contains('@') {
|
||||||
// TODO: need to support sort, filter etc for remote communities
|
// TODO: need to support sort, filter etc for remote communities
|
||||||
get_remote_community(name.to_owned())?
|
get_remote_community(name.to_owned())?
|
||||||
} else {
|
} else {
|
||||||
get_community.name = Some(name.replace("!", ""));
|
get_community.name = Some(name.replace("!", ""));
|
||||||
Oper::new(user_operation, get_community).perform()?
|
Oper::new(user_operation, get_community).perform(&conn)?
|
||||||
};
|
};
|
||||||
Ok(serde_json::to_string(&remote_community)?)
|
Ok(serde_json::to_string(&remote_community)?)
|
||||||
} else {
|
} else {
|
||||||
let res = Oper::new(user_operation, get_community).perform()?;
|
let res = Oper::new(user_operation, get_community).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,19 +404,19 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
Ok(serde_json::to_string(&val)?)
|
Ok(serde_json::to_string(&val)?)
|
||||||
} else {
|
} else {
|
||||||
let list_communities: ListCommunities = serde_json::from_str(data)?;
|
let list_communities: ListCommunities = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, list_communities).perform()?;
|
let res = Oper::new(user_operation, list_communities).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UserOperation::CreateCommunity => {
|
UserOperation::CreateCommunity => {
|
||||||
chat.check_rate_limit_register(msg.id)?;
|
chat.check_rate_limit_register(msg.id)?;
|
||||||
let create_community: CreateCommunity = serde_json::from_str(data)?;
|
let create_community: CreateCommunity = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, create_community).perform()?;
|
let res = Oper::new(user_operation, create_community).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::EditCommunity => {
|
UserOperation::EditCommunity => {
|
||||||
let edit_community: EditCommunity = serde_json::from_str(data)?;
|
let edit_community: EditCommunity = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, edit_community).perform()?;
|
let res = Oper::new(user_operation, edit_community).perform(&conn)?;
|
||||||
let mut community_sent: CommunityResponse = res.clone();
|
let mut community_sent: CommunityResponse = res.clone();
|
||||||
community_sent.community.user_id = None;
|
community_sent.community.user_id = None;
|
||||||
community_sent.community.subscribed = None;
|
community_sent.community.subscribed = None;
|
||||||
|
@ -405,18 +426,18 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
}
|
}
|
||||||
UserOperation::FollowCommunity => {
|
UserOperation::FollowCommunity => {
|
||||||
let follow_community: FollowCommunity = serde_json::from_str(data)?;
|
let follow_community: FollowCommunity = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, follow_community).perform()?;
|
let res = Oper::new(user_operation, follow_community).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::GetFollowedCommunities => {
|
UserOperation::GetFollowedCommunities => {
|
||||||
let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?;
|
let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, followed_communities).perform()?;
|
let res = Oper::new(user_operation, followed_communities).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::BanFromCommunity => {
|
UserOperation::BanFromCommunity => {
|
||||||
let ban_from_community: BanFromCommunity = serde_json::from_str(data)?;
|
let ban_from_community: BanFromCommunity = serde_json::from_str(data)?;
|
||||||
let community_id = ban_from_community.community_id;
|
let community_id = ban_from_community.community_id;
|
||||||
let res = Oper::new(user_operation, ban_from_community).perform()?;
|
let res = Oper::new(user_operation, ban_from_community).perform(&conn)?;
|
||||||
let res_str = serde_json::to_string(&res)?;
|
let res_str = serde_json::to_string(&res)?;
|
||||||
chat.send_community_message(community_id, &res_str, msg.id)?;
|
chat.send_community_message(community_id, &res_str, msg.id)?;
|
||||||
Ok(res_str)
|
Ok(res_str)
|
||||||
|
@ -424,42 +445,42 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
UserOperation::AddModToCommunity => {
|
UserOperation::AddModToCommunity => {
|
||||||
let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?;
|
let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?;
|
||||||
let community_id = mod_add_to_community.community_id;
|
let community_id = mod_add_to_community.community_id;
|
||||||
let res = Oper::new(user_operation, mod_add_to_community).perform()?;
|
let res = Oper::new(user_operation, mod_add_to_community).perform(&conn)?;
|
||||||
let res_str = serde_json::to_string(&res)?;
|
let res_str = serde_json::to_string(&res)?;
|
||||||
chat.send_community_message(community_id, &res_str, msg.id)?;
|
chat.send_community_message(community_id, &res_str, msg.id)?;
|
||||||
Ok(res_str)
|
Ok(res_str)
|
||||||
}
|
}
|
||||||
UserOperation::ListCategories => {
|
UserOperation::ListCategories => {
|
||||||
let list_categories: ListCategories = ListCategories;
|
let list_categories: ListCategories = ListCategories;
|
||||||
let res = Oper::new(user_operation, list_categories).perform()?;
|
let res = Oper::new(user_operation, list_categories).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::CreatePost => {
|
UserOperation::CreatePost => {
|
||||||
chat.check_rate_limit_post(msg.id)?;
|
chat.check_rate_limit_post(msg.id)?;
|
||||||
let create_post: CreatePost = serde_json::from_str(data)?;
|
let create_post: CreatePost = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, create_post).perform()?;
|
let res = Oper::new(user_operation, create_post).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::GetPost => {
|
UserOperation::GetPost => {
|
||||||
let get_post: GetPost = serde_json::from_str(data)?;
|
let get_post: GetPost = serde_json::from_str(data)?;
|
||||||
chat.join_room(get_post.id, msg.id);
|
chat.join_room(get_post.id, msg.id);
|
||||||
let res = Oper::new(user_operation, get_post).perform()?;
|
let res = Oper::new(user_operation, get_post).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::GetPosts => {
|
UserOperation::GetPosts => {
|
||||||
let get_posts: GetPosts = serde_json::from_str(data)?;
|
let get_posts: GetPosts = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, get_posts).perform()?;
|
let res = Oper::new(user_operation, get_posts).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::CreatePostLike => {
|
UserOperation::CreatePostLike => {
|
||||||
chat.check_rate_limit_message(msg.id)?;
|
chat.check_rate_limit_message(msg.id)?;
|
||||||
let create_post_like: CreatePostLike = serde_json::from_str(data)?;
|
let create_post_like: CreatePostLike = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, create_post_like).perform()?;
|
let res = Oper::new(user_operation, create_post_like).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::EditPost => {
|
UserOperation::EditPost => {
|
||||||
let edit_post: EditPost = serde_json::from_str(data)?;
|
let edit_post: EditPost = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, edit_post).perform()?;
|
let res = Oper::new(user_operation, edit_post).perform(&conn)?;
|
||||||
let mut post_sent = res.clone();
|
let mut post_sent = res.clone();
|
||||||
post_sent.post.my_vote = None;
|
post_sent.post.my_vote = None;
|
||||||
let post_sent_str = serde_json::to_string(&post_sent)?;
|
let post_sent_str = serde_json::to_string(&post_sent)?;
|
||||||
|
@ -468,14 +489,14 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
}
|
}
|
||||||
UserOperation::SavePost => {
|
UserOperation::SavePost => {
|
||||||
let save_post: SavePost = serde_json::from_str(data)?;
|
let save_post: SavePost = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, save_post).perform()?;
|
let res = Oper::new(user_operation, save_post).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::CreateComment => {
|
UserOperation::CreateComment => {
|
||||||
chat.check_rate_limit_message(msg.id)?;
|
chat.check_rate_limit_message(msg.id)?;
|
||||||
let create_comment: CreateComment = serde_json::from_str(data)?;
|
let create_comment: CreateComment = serde_json::from_str(data)?;
|
||||||
let post_id = create_comment.post_id;
|
let post_id = create_comment.post_id;
|
||||||
let res = Oper::new(user_operation, create_comment).perform()?;
|
let res = Oper::new(user_operation, create_comment).perform(&conn)?;
|
||||||
let mut comment_sent = res.clone();
|
let mut comment_sent = res.clone();
|
||||||
comment_sent.comment.my_vote = None;
|
comment_sent.comment.my_vote = None;
|
||||||
comment_sent.comment.user_id = None;
|
comment_sent.comment.user_id = None;
|
||||||
|
@ -486,7 +507,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
UserOperation::EditComment => {
|
UserOperation::EditComment => {
|
||||||
let edit_comment: EditComment = serde_json::from_str(data)?;
|
let edit_comment: EditComment = serde_json::from_str(data)?;
|
||||||
let post_id = edit_comment.post_id;
|
let post_id = edit_comment.post_id;
|
||||||
let res = Oper::new(user_operation, edit_comment).perform()?;
|
let res = Oper::new(user_operation, edit_comment).perform(&conn)?;
|
||||||
let mut comment_sent = res.clone();
|
let mut comment_sent = res.clone();
|
||||||
comment_sent.comment.my_vote = None;
|
comment_sent.comment.my_vote = None;
|
||||||
comment_sent.comment.user_id = None;
|
comment_sent.comment.user_id = None;
|
||||||
|
@ -496,14 +517,14 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
}
|
}
|
||||||
UserOperation::SaveComment => {
|
UserOperation::SaveComment => {
|
||||||
let save_comment: SaveComment = serde_json::from_str(data)?;
|
let save_comment: SaveComment = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, save_comment).perform()?;
|
let res = Oper::new(user_operation, save_comment).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::CreateCommentLike => {
|
UserOperation::CreateCommentLike => {
|
||||||
chat.check_rate_limit_message(msg.id)?;
|
chat.check_rate_limit_message(msg.id)?;
|
||||||
let create_comment_like: CreateCommentLike = serde_json::from_str(data)?;
|
let create_comment_like: CreateCommentLike = serde_json::from_str(data)?;
|
||||||
let post_id = create_comment_like.post_id;
|
let post_id = create_comment_like.post_id;
|
||||||
let res = Oper::new(user_operation, create_comment_like).perform()?;
|
let res = Oper::new(user_operation, create_comment_like).perform(&conn)?;
|
||||||
let mut comment_sent = res.clone();
|
let mut comment_sent = res.clone();
|
||||||
comment_sent.comment.my_vote = None;
|
comment_sent.comment.my_vote = None;
|
||||||
comment_sent.comment.user_id = None;
|
comment_sent.comment.user_id = None;
|
||||||
|
@ -513,54 +534,54 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
}
|
}
|
||||||
UserOperation::GetModlog => {
|
UserOperation::GetModlog => {
|
||||||
let get_modlog: GetModlog = serde_json::from_str(data)?;
|
let get_modlog: GetModlog = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, get_modlog).perform()?;
|
let res = Oper::new(user_operation, get_modlog).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::CreateSite => {
|
UserOperation::CreateSite => {
|
||||||
let create_site: CreateSite = serde_json::from_str(data)?;
|
let create_site: CreateSite = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, create_site).perform()?;
|
let res = Oper::new(user_operation, create_site).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::EditSite => {
|
UserOperation::EditSite => {
|
||||||
let edit_site: EditSite = serde_json::from_str(data)?;
|
let edit_site: EditSite = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, edit_site).perform()?;
|
let res = Oper::new(user_operation, edit_site).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::GetSite => {
|
UserOperation::GetSite => {
|
||||||
let online: usize = chat.sessions.len();
|
let online: usize = chat.sessions.len();
|
||||||
let get_site: GetSite = serde_json::from_str(data)?;
|
let get_site: GetSite = serde_json::from_str(data)?;
|
||||||
let mut res = Oper::new(user_operation, get_site).perform()?;
|
let mut res = Oper::new(user_operation, get_site).perform(&conn)?;
|
||||||
res.online = online;
|
res.online = online;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::Search => {
|
UserOperation::Search => {
|
||||||
let search: Search = serde_json::from_str(data)?;
|
let search: Search = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, search).perform()?;
|
let res = Oper::new(user_operation, search).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::TransferCommunity => {
|
UserOperation::TransferCommunity => {
|
||||||
let transfer_community: TransferCommunity = serde_json::from_str(data)?;
|
let transfer_community: TransferCommunity = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, transfer_community).perform()?;
|
let res = Oper::new(user_operation, transfer_community).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::TransferSite => {
|
UserOperation::TransferSite => {
|
||||||
let transfer_site: TransferSite = serde_json::from_str(data)?;
|
let transfer_site: TransferSite = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, transfer_site).perform()?;
|
let res = Oper::new(user_operation, transfer_site).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::DeleteAccount => {
|
UserOperation::DeleteAccount => {
|
||||||
let delete_account: DeleteAccount = serde_json::from_str(data)?;
|
let delete_account: DeleteAccount = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, delete_account).perform()?;
|
let res = Oper::new(user_operation, delete_account).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::PasswordReset => {
|
UserOperation::PasswordReset => {
|
||||||
let password_reset: PasswordReset = serde_json::from_str(data)?;
|
let password_reset: PasswordReset = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, password_reset).perform()?;
|
let res = Oper::new(user_operation, password_reset).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
UserOperation::PasswordChange => {
|
UserOperation::PasswordChange => {
|
||||||
let password_change: PasswordChange = serde_json::from_str(data)?;
|
let password_change: PasswordChange = serde_json::from_str(data)?;
|
||||||
let res = Oper::new(user_operation, password_change).perform()?;
|
let res = Oper::new(user_operation, password_change).perform(&conn)?;
|
||||||
Ok(serde_json::to_string(&res)?)
|
Ok(serde_json::to_string(&res)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
ui/package.json
vendored
18
ui/package.json
vendored
|
@ -15,33 +15,32 @@
|
||||||
"@types/autosize": "^3.0.6",
|
"@types/autosize": "^3.0.6",
|
||||||
"@types/js-cookie": "^2.2.1",
|
"@types/js-cookie": "^2.2.1",
|
||||||
"@types/jwt-decode": "^2.2.1",
|
"@types/jwt-decode": "^2.2.1",
|
||||||
"@types/markdown-it": "^0.0.7",
|
"@types/markdown-it": "^0.0.9",
|
||||||
"@types/markdown-it-container": "^2.0.2",
|
"@types/markdown-it-container": "^2.0.2",
|
||||||
"autosize": "^4.0.2",
|
"autosize": "^4.0.2",
|
||||||
"bootswatch": "^4.3.1",
|
"bootswatch": "^4.3.1",
|
||||||
"classcat": "^1.1.3",
|
"classcat": "^1.1.3",
|
||||||
"dotenv": "^6.1.0",
|
"dotenv": "^8.2.0",
|
||||||
"emoji-short-name": "^0.1.0",
|
"emoji-short-name": "^0.1.0",
|
||||||
"husky": "^3.0.9",
|
"husky": "^3.0.9",
|
||||||
"i18next": "^17.0.9",
|
"i18next": "^19.0.3",
|
||||||
"inferno": "^7.0.1",
|
"inferno": "^7.0.1",
|
||||||
"inferno-i18next": "nimbusec-oss/inferno-i18next",
|
"inferno-i18next": "nimbusec-oss/inferno-i18next",
|
||||||
"inferno-router": "^7.0.1",
|
"inferno-router": "^7.0.1",
|
||||||
"js-cookie": "^2.2.0",
|
"js-cookie": "^2.2.0",
|
||||||
"jwt-decode": "^2.2.0",
|
"jwt-decode": "^2.2.0",
|
||||||
"markdown-it": "^8.4.2",
|
"markdown-it": "^10.0.0",
|
||||||
"markdown-it-container": "^2.0.0",
|
"markdown-it-container": "^2.0.0",
|
||||||
"markdown-it-emoji": "^1.4.0",
|
"markdown-it-emoji": "^1.4.0",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"prettier": "^1.18.2",
|
"prettier": "^1.18.2",
|
||||||
"rxjs": "^6.4.0",
|
"rxjs": "^6.4.0",
|
||||||
"terser": "^3.17.0",
|
"terser": "^4.6.0",
|
||||||
"tributejs": "3.7.2",
|
"tributejs": "^4.1.1",
|
||||||
"twemoji": "^12.1.2",
|
"twemoji": "^12.1.2",
|
||||||
"ws": "^7.0.0"
|
"ws": "^7.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/i18next": "^12.1.0",
|
|
||||||
"eslint": "^6.5.1",
|
"eslint": "^6.5.1",
|
||||||
"eslint-plugin-inferno": "^7.14.3",
|
"eslint-plugin-inferno": "^7.14.3",
|
||||||
"eslint-plugin-jane": "^7.0.0",
|
"eslint-plugin-jane": "^7.0.0",
|
||||||
|
@ -58,7 +57,7 @@
|
||||||
"engineStrict": true,
|
"engineStrict": true,
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"pre-commit": "lint-staged"
|
"pre-commit": "cargo fmt --manifest-path ../server/Cargo.toml && cargo clippy --manifest-path ../server/Cargo.toml --all-targets --all-features -- -D warnings && lint-staged"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
@ -67,6 +66,9 @@
|
||||||
"eslint --fix",
|
"eslint --fix",
|
||||||
"git add"
|
"git add"
|
||||||
],
|
],
|
||||||
|
"../server/src/**/*.rs": [
|
||||||
|
"git add"
|
||||||
|
],
|
||||||
"package.json": [
|
"package.json": [
|
||||||
"sortpack",
|
"sortpack",
|
||||||
"git add"
|
"git add"
|
||||||
|
|
4
ui/src/components/comment-form.tsx
vendored
4
ui/src/components/comment-form.tsx
vendored
|
@ -18,11 +18,11 @@ import {
|
||||||
markdownHelpUrl,
|
markdownHelpUrl,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import * as autosize from 'autosize';
|
import autosize from 'autosize';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
import Tribute from 'tributejs/src/Tribute.js';
|
import Tribute from 'tributejs/src/Tribute.js';
|
||||||
import * as emojiShortName from 'emoji-short-name';
|
import emojiShortName from 'emoji-short-name';
|
||||||
|
|
||||||
interface CommentFormProps {
|
interface CommentFormProps {
|
||||||
postId?: number;
|
postId?: number;
|
||||||
|
|
5
ui/src/components/comment-node.tsx
vendored
5
ui/src/components/comment-node.tsx
vendored
|
@ -23,8 +23,9 @@ import {
|
||||||
canMod,
|
canMod,
|
||||||
isMod,
|
isMod,
|
||||||
pictshareAvatarThumbnail,
|
pictshareAvatarThumbnail,
|
||||||
|
showAvatars,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import * as moment from 'moment';
|
import moment from 'moment';
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
import { CommentForm } from './comment-form';
|
import { CommentForm } from './comment-form';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
|
@ -138,7 +139,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
className="text-info"
|
className="text-info"
|
||||||
to={`/u/${node.comment.creator_name}`}
|
to={`/u/${node.comment.creator_name}`}
|
||||||
>
|
>
|
||||||
{node.comment.creator_avatar && (
|
{node.comment.creator_avatar && showAvatars() && (
|
||||||
<img
|
<img
|
||||||
height="32"
|
height="32"
|
||||||
width="32"
|
width="32"
|
||||||
|
|
10
ui/src/components/community-form.tsx
vendored
10
ui/src/components/community-form.tsx
vendored
|
@ -7,6 +7,7 @@ import {
|
||||||
Category,
|
Category,
|
||||||
ListCategoriesResponse,
|
ListCategoriesResponse,
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
|
GetSiteResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService } from '../services';
|
||||||
import { msgOp, capitalizeFirstLetter } from '../utils';
|
import { msgOp, capitalizeFirstLetter } from '../utils';
|
||||||
|
@ -27,6 +28,7 @@ interface CommunityFormState {
|
||||||
communityForm: CommunityFormI;
|
communityForm: CommunityFormI;
|
||||||
categories: Array<Category>;
|
categories: Array<Category>;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
enable_nsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommunityForm extends Component<
|
export class CommunityForm extends Component<
|
||||||
|
@ -44,6 +46,7 @@ export class CommunityForm extends Component<
|
||||||
},
|
},
|
||||||
categories: [],
|
categories: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
|
enable_nsfw: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -79,6 +82,7 @@ export class CommunityForm extends Component<
|
||||||
);
|
);
|
||||||
|
|
||||||
WebSocketService.Instance.listCategories();
|
WebSocketService.Instance.listCategories();
|
||||||
|
WebSocketService.Instance.getSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -157,7 +161,7 @@ export class CommunityForm extends Component<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{WebSocketService.Instance.site.enable_nsfw && (
|
{this.state.enable_nsfw && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
@ -267,6 +271,10 @@ export class CommunityForm extends Component<
|
||||||
let res: CommunityResponse = msg;
|
let res: CommunityResponse = msg;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.props.onEdit(res.community);
|
this.props.onEdit(res.community);
|
||||||
|
} else if (op == UserOperation.GetSite) {
|
||||||
|
let res: GetSiteResponse = msg;
|
||||||
|
this.state.enable_nsfw = res.site.enable_nsfw;
|
||||||
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
ui/src/components/login.tsx
vendored
20
ui/src/components/login.tsx
vendored
|
@ -7,6 +7,7 @@ import {
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
PasswordResetForm,
|
PasswordResetForm,
|
||||||
|
GetSiteResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { msgOp, validEmail } from '../utils';
|
import { msgOp, validEmail } from '../utils';
|
||||||
|
@ -18,6 +19,7 @@ interface State {
|
||||||
registerForm: RegisterForm;
|
registerForm: RegisterForm;
|
||||||
loginLoading: boolean;
|
loginLoading: boolean;
|
||||||
registerLoading: boolean;
|
registerLoading: boolean;
|
||||||
|
enable_nsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Login extends Component<any, State> {
|
export class Login extends Component<any, State> {
|
||||||
|
@ -37,6 +39,7 @@ export class Login extends Component<any, State> {
|
||||||
},
|
},
|
||||||
loginLoading: false,
|
loginLoading: false,
|
||||||
registerLoading: false,
|
registerLoading: false,
|
||||||
|
enable_nsfw: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -58,18 +61,14 @@ export class Login extends Component<any, State> {
|
||||||
err => console.error(err),
|
err => console.error(err),
|
||||||
() => console.log('complete')
|
() => console.log('complete')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
WebSocketService.Instance.getSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.title = `${i18n.t('login')} - ${
|
|
||||||
WebSocketService.Instance.site.name
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -205,7 +204,7 @@ export class Login extends Component<any, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{WebSocketService.Instance.site.enable_nsfw && (
|
{this.state.enable_nsfw && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
@ -322,6 +321,13 @@ export class Login extends Component<any, State> {
|
||||||
this.props.history.push('/communities');
|
this.props.history.push('/communities');
|
||||||
} else if (op == UserOperation.PasswordReset) {
|
} else if (op == UserOperation.PasswordReset) {
|
||||||
alert(i18n.t('reset_password_mail_sent'));
|
alert(i18n.t('reset_password_mail_sent'));
|
||||||
|
} else if (op == UserOperation.GetSite) {
|
||||||
|
let res: GetSiteResponse = msg;
|
||||||
|
this.state.enable_nsfw = res.site.enable_nsfw;
|
||||||
|
this.setState(this.state);
|
||||||
|
document.title = `${i18n.t('login')} - ${
|
||||||
|
WebSocketService.Instance.site.name
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
ui/src/components/main.tsx
vendored
3
ui/src/components/main.tsx
vendored
|
@ -32,6 +32,7 @@ import {
|
||||||
routeListingTypeToEnum,
|
routeListingTypeToEnum,
|
||||||
postRefetchSeconds,
|
postRefetchSeconds,
|
||||||
pictshareAvatarThumbnail,
|
pictshareAvatarThumbnail,
|
||||||
|
showAvatars,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
@ -345,7 +346,7 @@ export class Main extends Component<any, MainState> {
|
||||||
{this.state.site.admins.map(admin => (
|
{this.state.site.admins.map(admin => (
|
||||||
<li class="list-inline-item">
|
<li class="list-inline-item">
|
||||||
<Link class="text-info" to={`/u/${admin.name}`}>
|
<Link class="text-info" to={`/u/${admin.name}`}>
|
||||||
{admin.avatar && (
|
{admin.avatar && showAvatars() && (
|
||||||
<img
|
<img
|
||||||
height="32"
|
height="32"
|
||||||
width="32"
|
width="32"
|
||||||
|
|
4
ui/src/components/navbar.tsx
vendored
4
ui/src/components/navbar.tsx
vendored
|
@ -13,7 +13,7 @@ import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
Comment,
|
Comment,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { msgOp, pictshareAvatarThumbnail } from '../utils';
|
import { msgOp, pictshareAvatarThumbnail, showAvatars } from '../utils';
|
||||||
import { version } from '../version';
|
import { version } from '../version';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
@ -152,7 +152,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
to={`/u/${UserService.Instance.user.username}`}
|
to={`/u/${UserService.Instance.user.username}`}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{UserService.Instance.user.avatar && (
|
{UserService.Instance.user.avatar && showAvatars() && (
|
||||||
<img
|
<img
|
||||||
src={pictshareAvatarThumbnail(
|
src={pictshareAvatarThumbnail(
|
||||||
UserService.Instance.user.avatar
|
UserService.Instance.user.avatar
|
||||||
|
|
12
ui/src/components/post-form.tsx
vendored
12
ui/src/components/post-form.tsx
vendored
|
@ -15,6 +15,7 @@ import {
|
||||||
SearchForm,
|
SearchForm,
|
||||||
SearchType,
|
SearchType,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
|
GetSiteResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import {
|
import {
|
||||||
|
@ -28,7 +29,7 @@ import {
|
||||||
debounce,
|
debounce,
|
||||||
isImage,
|
isImage,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import * as autosize from 'autosize';
|
import autosize from 'autosize';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ interface PostFormState {
|
||||||
suggestedTitle: string;
|
suggestedTitle: string;
|
||||||
suggestedPosts: Array<Post>;
|
suggestedPosts: Array<Post>;
|
||||||
crossPosts: Array<Post>;
|
crossPosts: Array<Post>;
|
||||||
|
enable_nsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostForm extends Component<PostFormProps, PostFormState> {
|
export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
|
@ -70,6 +72,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
suggestedTitle: undefined,
|
suggestedTitle: undefined,
|
||||||
suggestedPosts: [],
|
suggestedPosts: [],
|
||||||
crossPosts: [],
|
crossPosts: [],
|
||||||
|
enable_nsfw: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -124,6 +127,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketService.Instance.listCommunities(listCommunitiesForm);
|
WebSocketService.Instance.listCommunities(listCommunitiesForm);
|
||||||
|
WebSocketService.Instance.getSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -287,7 +291,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{WebSocketService.Instance.site.enable_nsfw && (
|
{this.state.enable_nsfw && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
@ -498,6 +502,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.state.crossPosts = res.posts;
|
this.state.crossPosts = res.posts;
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.GetSite) {
|
||||||
|
let res: GetSiteResponse = msg;
|
||||||
|
this.state.enable_nsfw = res.site.enable_nsfw;
|
||||||
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
6
ui/src/components/post-listing.tsx
vendored
6
ui/src/components/post-listing.tsx
vendored
|
@ -26,6 +26,8 @@ import {
|
||||||
isVideo,
|
isVideo,
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
pictshareAvatarThumbnail,
|
pictshareAvatarThumbnail,
|
||||||
|
showAvatars,
|
||||||
|
imageThumbnailer,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
@ -136,7 +138,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="mx-2 mt-1 float-left img-fluid thumbnail rounded"
|
class="mx-2 mt-1 float-left img-fluid thumbnail rounded"
|
||||||
src={post.url}
|
src={imageThumbnailer(post.url)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -249,7 +251,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span>{i18n.t('by')} </span>
|
<span>{i18n.t('by')} </span>
|
||||||
<Link className="text-info" to={`/u/${post.creator_name}`}>
|
<Link className="text-info" to={`/u/${post.creator_name}`}>
|
||||||
{post.creator_avatar && (
|
{post.creator_avatar && showAvatars() && (
|
||||||
<img
|
<img
|
||||||
height="32"
|
height="32"
|
||||||
width="32"
|
width="32"
|
||||||
|
|
3
ui/src/components/post.tsx
vendored
3
ui/src/components/post.tsx
vendored
|
@ -34,7 +34,7 @@ import { PostListings } from './post-listings';
|
||||||
import { Sidebar } from './sidebar';
|
import { Sidebar } from './sidebar';
|
||||||
import { CommentForm } from './comment-form';
|
import { CommentForm } from './comment-form';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
import * as autosize from 'autosize';
|
import autosize from 'autosize';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
|
||||||
|
@ -169,7 +169,6 @@ export class Post extends Component<any, PostState> {
|
||||||
post={this.state.post}
|
post={this.state.post}
|
||||||
showBody
|
showBody
|
||||||
showCommunity
|
showCommunity
|
||||||
editable
|
|
||||||
moderators={this.state.moderators}
|
moderators={this.state.moderators}
|
||||||
admins={this.state.admins}
|
admins={this.state.admins}
|
||||||
/>
|
/>
|
||||||
|
|
3
ui/src/components/search.tsx
vendored
3
ui/src/components/search.tsx
vendored
|
@ -20,6 +20,7 @@ import {
|
||||||
routeSearchTypeToEnum,
|
routeSearchTypeToEnum,
|
||||||
routeSortTypeToEnum,
|
routeSortTypeToEnum,
|
||||||
pictshareAvatarThumbnail,
|
pictshareAvatarThumbnail,
|
||||||
|
showAvatars,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
import { PostListing } from './post-listing';
|
||||||
import { SortSelect } from './sort-select';
|
import { SortSelect } from './sort-select';
|
||||||
|
@ -288,7 +289,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
className="text-info"
|
className="text-info"
|
||||||
to={`/u/${(i.data as UserView).name}`}
|
to={`/u/${(i.data as UserView).name}`}
|
||||||
>
|
>
|
||||||
{(i.data as UserView).avatar && (
|
{(i.data as UserView).avatar && showAvatars() && (
|
||||||
<img
|
<img
|
||||||
height="32"
|
height="32"
|
||||||
width="32"
|
width="32"
|
||||||
|
|
9
ui/src/components/sidebar.tsx
vendored
9
ui/src/components/sidebar.tsx
vendored
|
@ -8,7 +8,12 @@ import {
|
||||||
UserView,
|
UserView,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { mdToHtml, getUnixTime, pictshareAvatarThumbnail } from '../utils';
|
import {
|
||||||
|
mdToHtml,
|
||||||
|
getUnixTime,
|
||||||
|
pictshareAvatarThumbnail,
|
||||||
|
showAvatars,
|
||||||
|
} from '../utils';
|
||||||
import { CommunityForm } from './community-form';
|
import { CommunityForm } from './community-form';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
@ -194,7 +199,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
{this.props.moderators.map(mod => (
|
{this.props.moderators.map(mod => (
|
||||||
<li class="list-inline-item">
|
<li class="list-inline-item">
|
||||||
<Link class="text-info" to={`/u/${mod.user_name}`}>
|
<Link class="text-info" to={`/u/${mod.user_name}`}>
|
||||||
{mod.avatar && (
|
{mod.avatar && showAvatars() && (
|
||||||
<img
|
<img
|
||||||
height="32"
|
height="32"
|
||||||
width="32"
|
width="32"
|
||||||
|
|
2
ui/src/components/site-form.tsx
vendored
2
ui/src/components/site-form.tsx
vendored
|
@ -2,7 +2,7 @@ import { Component, linkEvent } from 'inferno';
|
||||||
import { Site, SiteForm as SiteFormI } from '../interfaces';
|
import { Site, SiteForm as SiteFormI } from '../interfaces';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService } from '../services';
|
||||||
import { capitalizeFirstLetter } from '../utils';
|
import { capitalizeFirstLetter } from '../utils';
|
||||||
import * as autosize from 'autosize';
|
import autosize from 'autosize';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import { T } from 'inferno-i18next';
|
import { T } from 'inferno-i18next';
|
||||||
|
|
||||||
|
|
56
ui/src/components/user.tsx
vendored
56
ui/src/components/user.tsx
vendored
|
@ -28,6 +28,7 @@ import {
|
||||||
themes,
|
themes,
|
||||||
setTheme,
|
setTheme,
|
||||||
languages,
|
languages,
|
||||||
|
showAvatars,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
import { PostListing } from './post-listing';
|
||||||
import { SortSelect } from './sort-select';
|
import { SortSelect } from './sort-select';
|
||||||
|
@ -80,6 +81,8 @@ export class User extends Component<any, UserState> {
|
||||||
comment_score: null,
|
comment_score: null,
|
||||||
banned: null,
|
banned: null,
|
||||||
avatar: null,
|
avatar: null,
|
||||||
|
show_avatars: null,
|
||||||
|
send_notifications_to_email: null,
|
||||||
},
|
},
|
||||||
user_id: null,
|
user_id: null,
|
||||||
username: null,
|
username: null,
|
||||||
|
@ -99,6 +102,8 @@ export class User extends Component<any, UserState> {
|
||||||
default_sort_type: null,
|
default_sort_type: null,
|
||||||
default_listing_type: null,
|
default_listing_type: null,
|
||||||
lang: null,
|
lang: null,
|
||||||
|
show_avatars: null,
|
||||||
|
send_notifications_to_email: null,
|
||||||
auth: null,
|
auth: null,
|
||||||
},
|
},
|
||||||
userSettingsLoading: null,
|
userSettingsLoading: null,
|
||||||
|
@ -207,7 +212,7 @@ export class User extends Component<any, UserState> {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-8">
|
<div class="col-12 col-md-8">
|
||||||
<h5>
|
<h5>
|
||||||
{this.state.user.avatar && (
|
{this.state.user.avatar && showAvatars() && (
|
||||||
<img
|
<img
|
||||||
height="80"
|
height="80"
|
||||||
width="80"
|
width="80"
|
||||||
|
@ -610,6 +615,41 @@ export class User extends Component<any, UserState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.userSettingsForm.show_avatars}
|
||||||
|
onChange={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleUserSettingsShowAvatarsChange
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<label class="form-check-label">
|
||||||
|
<T i18nKey="show_avatars">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
disabled={!this.state.user.email}
|
||||||
|
checked={
|
||||||
|
this.state.userSettingsForm.send_notifications_to_email
|
||||||
|
}
|
||||||
|
onChange={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleUserSettingsSendNotificationsToEmailChange
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<label class="form-check-label">
|
||||||
|
<T i18nKey="send_notifications_to_email">#</T>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="submit" class="btn btn-block btn-secondary mr-4">
|
<button type="submit" class="btn btn-block btn-secondary mr-4">
|
||||||
{this.state.userSettingsLoading ? (
|
{this.state.userSettingsLoading ? (
|
||||||
|
@ -804,6 +844,17 @@ export class User extends Component<any, UserState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleUserSettingsShowAvatarsChange(i: User, event: any) {
|
||||||
|
i.state.userSettingsForm.show_avatars = event.target.checked;
|
||||||
|
UserService.Instance.user.show_avatars = event.target.checked; // Just for instant updates
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUserSettingsSendNotificationsToEmailChange(i: User, event: any) {
|
||||||
|
i.state.userSettingsForm.send_notifications_to_email = event.target.checked;
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleUserSettingsThemeChange(i: User, event: any) {
|
handleUserSettingsThemeChange(i: User, event: any) {
|
||||||
i.state.userSettingsForm.theme = event.target.value;
|
i.state.userSettingsForm.theme = event.target.value;
|
||||||
setTheme(event.target.value);
|
setTheme(event.target.value);
|
||||||
|
@ -957,6 +1008,9 @@ export class User extends Component<any, UserState> {
|
||||||
this.state.userSettingsForm.lang = UserService.Instance.user.lang;
|
this.state.userSettingsForm.lang = UserService.Instance.user.lang;
|
||||||
this.state.userSettingsForm.avatar = UserService.Instance.user.avatar;
|
this.state.userSettingsForm.avatar = UserService.Instance.user.avatar;
|
||||||
this.state.userSettingsForm.email = this.state.user.email;
|
this.state.userSettingsForm.email = this.state.user.email;
|
||||||
|
this.state.userSettingsForm.send_notifications_to_email = this.state.user.send_notifications_to_email;
|
||||||
|
this.state.userSettingsForm.show_avatars =
|
||||||
|
UserService.Instance.user.show_avatars;
|
||||||
}
|
}
|
||||||
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
|
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
|
|
6
ui/src/i18next.ts
vendored
6
ui/src/i18next.ts
vendored
|
@ -1,4 +1,4 @@
|
||||||
import * as i18n from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { getLanguage } from './utils';
|
import { getLanguage } from './utils';
|
||||||
import { en } from './translations/en';
|
import { en } from './translations/en';
|
||||||
import { eo } from './translations/eo';
|
import { eo } from './translations/eo';
|
||||||
|
@ -30,7 +30,7 @@ function format(value: any, format: any, lng: any) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
i18n.init({
|
i18next.init({
|
||||||
debug: false,
|
debug: false,
|
||||||
// load: 'languageOnly',
|
// load: 'languageOnly',
|
||||||
|
|
||||||
|
@ -43,4 +43,4 @@ i18n.init({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export { i18n, resources };
|
export { i18next as i18n, resources };
|
||||||
|
|
5
ui/src/interfaces.ts
vendored
5
ui/src/interfaces.ts
vendored
|
@ -81,6 +81,7 @@ export interface User {
|
||||||
default_listing_type: ListingType;
|
default_listing_type: ListingType;
|
||||||
lang: string;
|
lang: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
|
show_avatars: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserView {
|
export interface UserView {
|
||||||
|
@ -95,6 +96,8 @@ export interface UserView {
|
||||||
number_of_comments: number;
|
number_of_comments: number;
|
||||||
comment_score: number;
|
comment_score: number;
|
||||||
banned: boolean;
|
banned: boolean;
|
||||||
|
show_avatars: boolean;
|
||||||
|
send_notifications_to_email: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommunityUser {
|
export interface CommunityUser {
|
||||||
|
@ -486,6 +489,8 @@ export interface UserSettingsForm {
|
||||||
new_password?: string;
|
new_password?: string;
|
||||||
new_password_verify?: string;
|
new_password_verify?: string;
|
||||||
old_password?: string;
|
old_password?: string;
|
||||||
|
show_avatars: boolean;
|
||||||
|
send_notifications_to_email: boolean;
|
||||||
auth: string;
|
auth: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
ui/src/services/UserService.ts
vendored
4
ui/src/services/UserService.ts
vendored
|
@ -1,7 +1,7 @@
|
||||||
import * as Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { User, LoginResponse } from '../interfaces';
|
import { User, LoginResponse } from '../interfaces';
|
||||||
import { setTheme } from '../utils';
|
import { setTheme } from '../utils';
|
||||||
import * as jwt_decode from 'jwt-decode';
|
import jwt_decode from 'jwt-decode';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export class UserService {
|
export class UserService {
|
||||||
|
|
2
ui/src/translations/en.ts
vendored
2
ui/src/translations/en.ts
vendored
|
@ -29,6 +29,7 @@ export const en = {
|
||||||
preview: 'Preview',
|
preview: 'Preview',
|
||||||
upload_image: 'upload image',
|
upload_image: 'upload image',
|
||||||
avatar: 'Avatar',
|
avatar: 'Avatar',
|
||||||
|
show_avatars: 'Show Avatars',
|
||||||
formatting_help: 'formatting help',
|
formatting_help: 'formatting help',
|
||||||
view_source: 'view source',
|
view_source: 'view source',
|
||||||
unlock: 'unlock',
|
unlock: 'unlock',
|
||||||
|
@ -126,6 +127,7 @@ export const en = {
|
||||||
new_password: 'New Password',
|
new_password: 'New Password',
|
||||||
no_email_setup: "This server hasn't correctly set up email.",
|
no_email_setup: "This server hasn't correctly set up email.",
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
|
send_notifications_to_email: 'Send notifications to Email',
|
||||||
optional: 'Optional',
|
optional: 'Optional',
|
||||||
expires: 'Expires',
|
expires: 'Expires',
|
||||||
language: 'Language',
|
language: 'Language',
|
||||||
|
|
30
ui/src/utils.ts
vendored
30
ui/src/utils.ts
vendored
|
@ -17,9 +17,9 @@ import {
|
||||||
SearchType,
|
SearchType,
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import { UserService } from './services/UserService';
|
import { UserService } from './services/UserService';
|
||||||
import * as markdown_it from 'markdown-it';
|
import markdown_it from 'markdown-it';
|
||||||
import * as markdownitEmoji from 'markdown-it-emoji/light';
|
import markdownitEmoji from 'markdown-it-emoji/light';
|
||||||
import * as markdown_it_container from 'markdown-it-container';
|
import markdown_it_container from 'markdown-it-container';
|
||||||
import * as twemoji from 'twemoji';
|
import * as twemoji from 'twemoji';
|
||||||
import * as emojiShortName from 'emoji-short-name';
|
import * as emojiShortName from 'emoji-short-name';
|
||||||
|
|
||||||
|
@ -58,9 +58,7 @@ export const md = new markdown_it({
|
||||||
|
|
||||||
if (tokens[idx].nesting === 1) {
|
if (tokens[idx].nesting === 1) {
|
||||||
// opening tag
|
// opening tag
|
||||||
return (
|
return `<details><summary> ${md.utils.escapeHtml(m[1])} </summary>\n`;
|
||||||
'<details><summary>' + md.utils.escapeHtml(m[1]) + '</summary>\n'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// closing tag
|
// closing tag
|
||||||
return '</details>\n';
|
return '</details>\n';
|
||||||
|
@ -266,7 +264,7 @@ export function getLanguage(): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBrowserLanguage(): string {
|
export function getBrowserLanguage(): string {
|
||||||
return navigator.language || navigator.userLanguage;
|
return navigator.language;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMomentLanguage(): string {
|
export function getMomentLanguage(): string {
|
||||||
|
@ -345,3 +343,21 @@ export function pictshareAvatarThumbnail(src: string): string {
|
||||||
let out = `${split[0]}pictshare/96x96${split[1]}`;
|
let out = `${split[0]}pictshare/96x96${split[1]}`;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function showAvatars(): boolean {
|
||||||
|
return (
|
||||||
|
(UserService.Instance.user && UserService.Instance.user.show_avatars) ||
|
||||||
|
!UserService.Instance.user
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to image thumbnail (only supports pictshare currently)
|
||||||
|
export function imageThumbnailer(url: string): string {
|
||||||
|
let split = url.split('pictshare');
|
||||||
|
if (split.length > 1) {
|
||||||
|
let out = `${split[0]}pictshare/140x140${split[1]}`;
|
||||||
|
return out;
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
2
ui/src/version.ts
vendored
2
ui/src/version.ts
vendored
|
@ -1 +1 @@
|
||||||
export let version: string = 'v0.5.14';
|
export let version: string = 'v0.5.19';
|
||||||
|
|
3
ui/tsconfig.json
vendored
3
ui/tsconfig.json
vendored
|
@ -7,6 +7,7 @@
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true,
|
||||||
|
"esModuleInterop": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1932
ui/yarn.lock
vendored
1932
ui/yarn.lock
vendored
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue