diff --git a/README.md b/README.md
index 3668d147e..d4ccab2e6 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/LemmyNet/lemmy.svg)
-[![Build Status](https://travis-ci.org/LemmyNet/lemmy.svg?branch=master)](https://travis-ci.org/LemmyNet/lemmy)
+[![Build Status](https://travis-ci.org/LemmyNet/lemmy.svg?branch=main)](https://travis-ci.org/LemmyNet/lemmy)
[![GitHub issues](https://img.shields.io/github/issues-raw/LemmyNet/lemmy.svg)](https://github.com/LemmyNet/lemmy/issues)
[![Docker Pulls](https://img.shields.io/docker/pulls/dessalines/lemmy.svg)](https://cloud.docker.com/repository/docker/dessalines/lemmy/)
[![Translation status](http://weblate.yerbamate.dev/widgets/lemmy/-/lemmy/svg-badge.svg)](http://weblate.yerbamate.dev/engage/lemmy/)
@@ -26,7 +26,7 @@
·
Request Feature
·
-
Releases
+
Releases
@@ -34,7 +34,7 @@
Front Page|Post
---|---
-![main screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/main_screen.png)|![chat screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/chat_screen.png)
+![main screen](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/main_screen.png)|![chat screen](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/chat_screen.png)
[Lemmy](https://github.com/LemmyNet/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
diff --git a/ansible/VERSION b/ansible/VERSION
index 51ae6cf92..ced68c150 100644
--- a/ansible/VERSION
+++ b/ansible/VERSION
@@ -1 +1 @@
-v0.7.20
+v0.7.26
diff --git a/ansible/templates/nginx.conf b/ansible/templates/nginx.conf
index 5847bad01..eaaa6b79e 100644
--- a/ansible/templates/nginx.conf
+++ b/ansible/templates/nginx.conf
@@ -1,4 +1,3 @@
-proxy_cache_path /var/cache/lemmy_frontend levels=1:2 keys_zone=lemmy_frontend_cache:10m max_size=100m use_temp_path=off;
limit_req_zone $binary_remote_addr zone=lemmy_ratelimit:10m rate=1r/s;
server {
@@ -65,13 +64,6 @@ server {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_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;
}
# Redirect pictshare images to pictrs
diff --git a/docker/prod/deploy.sh b/docker/prod/deploy.sh
index 2c6e3d312..2e8728180 100755
--- a/docker/prod/deploy.sh
+++ b/docker/prod/deploy.sh
@@ -1,10 +1,10 @@
#!/bin/sh
set -e
-git checkout master
+git checkout main
# Import translations
git fetch weblate
-git merge weblate/master
+git merge weblate/main
# Creating the new tag
new_tag="$1"
@@ -12,8 +12,6 @@ third_semver=$(echo $new_tag | cut -d "." -f 3)
# Setting the version on the front end
cd ../../
-echo "export const version: string = '$new_tag';" > "ui/src/version.ts"
-git add "ui/src/version.ts"
# Setting the version on the backend
echo "pub const VERSION: &str = \"$new_tag\";" > "server/src/version.rs"
git add "server/src/version.rs"
diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml
index 7feb65d33..3affe5f06 100644
--- a/docker/prod/docker-compose.yml
+++ b/docker/prod/docker-compose.yml
@@ -12,7 +12,7 @@ services:
restart: always
lemmy:
- image: dessalines/lemmy:v0.7.20
+ image: dessalines/lemmy:v0.7.26
ports:
- "127.0.0.1:8536:8536"
restart: always
diff --git a/docs/src/about.md b/docs/src/about.md
index 2c0e418b3..8db35b4f6 100644
--- a/docs/src/about.md
+++ b/docs/src/about.md
@@ -2,7 +2,7 @@
Front Page|Post
---|---
-![main screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/main_screen.png)|![chat screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/chat_screen.png)
+![main screen](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/main_screen.png)|![chat screen](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/chat_screen.png)
[Lemmy](https://github.com/LemmyNet/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
diff --git a/docs/src/about_guide.md b/docs/src/about_guide.md
index 6f709b580..3c5e34b4b 100644
--- a/docs/src/about_guide.md
+++ b/docs/src/about_guide.md
@@ -35,6 +35,8 @@ Horizontal Rule
\--- | Horizontal Rule
\*\*\* | Horizontal Rule
\`Inline code\` with backticks | |`Inline code` with backticks
\`\`\`
\# code block
print '3 backticks or'
print 'indent 4 spaces'
\`\`\` | ····\# code block
····print '3 backticks or'
····print 'indent 4 spaces' | \# code block
print '3 backticks or'
print 'indent 4 spaces'
::: spoiler hidden or nsfw stuff
*a bunch of spoilers here*
::: | |
hidden or nsfw stuff
a bunch of spoilers here
+Some ~subscript~ text | | Some
subscript text
+Some ^superscript^ text | | Some
superscript text
[CommonMark Tutorial](https://commonmark.org/help/tutorial/)
diff --git a/docs/src/about_ranking.md b/docs/src/about_ranking.md
index fe9e82bbb..f1ed9b386 100644
--- a/docs/src/about_ranking.md
+++ b/docs/src/about_ranking.md
@@ -26,4 +26,4 @@ Gravity = Decay gravity, 1.8 is default
A plot of rank over 24 hours, of scores of 1, 5, 10, 100, 1000, with a scale factor of 10k.
-![](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/rank_algorithm.png)
+![](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/rank_algorithm.png)
diff --git a/docs/src/administration_configuration.md b/docs/src/administration_configuration.md
index cc4c56898..c2aef3f7f 100644
--- a/docs/src/administration_configuration.md
+++ b/docs/src/administration_configuration.md
@@ -1,7 +1,7 @@
# Configuration
The configuration is based on the file
-[defaults.hjson](https://yerbamate.dev/LemmyNet/lemmy/src/branch/master/server/config/defaults.hjson).
+[defaults.hjson](https://yerbamate.dev/LemmyNet/lemmy/src/branch/main/server/config/defaults.hjson).
This file also contains documentation for all the available options. To override the defaults, you
can copy the options you want to change into your local `config.hjson` file.
diff --git a/docs/src/administration_install_ansible.md b/docs/src/administration_install_ansible.md
index 4676f47d4..849957ad1 100644
--- a/docs/src/administration_install_ansible.md
+++ b/docs/src/administration_install_ansible.md
@@ -19,7 +19,7 @@ ansible-playbook lemmy.yml --become
To update to a new version, just run the following in your local Lemmy repo:
```bash
-git pull origin master
+git pull origin main
cd ansible
ansible-playbook lemmy.yml --become
```
diff --git a/docs/src/administration_install_docker.md b/docs/src/administration_install_docker.md
index a2bed794f..ae0375a81 100644
--- a/docs/src/administration_install_docker.md
+++ b/docs/src/administration_install_docker.md
@@ -8,9 +8,9 @@ mkdir /lemmy
cd /lemmy
# download default config files
-wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/prod/docker-compose.yml
-wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/lemmy.hjson
-wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/iframely.config.local.js
+wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/prod/docker-compose.yml
+wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/lemmy.hjson
+wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/iframely.config.local.js
# Set correct permissions for pictrs folder
mkdir -p volumes/pictrs
@@ -21,10 +21,10 @@ After this, have a look at the [config file](administration_configuration.md) na
`docker-compose up -d`
-To make Lemmy available outside the server, you need to setup a reverse proxy, like Nginx. [A sample nginx config](https://raw.githubusercontent.com/LemmyNet/lemmy/master/ansible/templates/nginx.conf), could be setup with:
+To make Lemmy available outside the server, you need to setup a reverse proxy, like Nginx. [A sample nginx config](https://raw.githubusercontent.com/LemmyNet/lemmy/main/ansible/templates/nginx.conf), could be setup with:
```bash
-wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/ansible/templates/nginx.conf
+wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/ansible/templates/nginx.conf
# Replace the {{ vars }}
sudo mv nginx.conf /etc/nginx/sites-enabled/lemmy.conf
```
@@ -36,6 +36,6 @@ You will also need to setup TLS, for example with [Let's Encrypt](https://letsen
To update to the newest version, you can manually change the version in `docker-compose.yml`. Alternatively, fetch the latest version from our git repo:
```bash
-wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/prod/docker-compose.yml
+wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/prod/docker-compose.yml
docker-compose up -d
```
diff --git a/docs/src/contributing_local_development.md b/docs/src/contributing_local_development.md
index 066386f50..9bdeff9ad 100644
--- a/docs/src/contributing_local_development.md
+++ b/docs/src/contributing_local_development.md
@@ -1,16 +1,26 @@
-### Ubuntu
-
-
-#### Build requirements:
+### Install build requirements
+#### Ubuntu
```
-sudo apt install git cargo libssl-dev pkg-config libpq-dev yarn curl gnupg2 git
+sudo apt install git cargo libssl-dev pkg-config libpq-dev yarn curl gnupg2
# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
```
-#### Get the source code
+#### macOS
+
+Install Rust using [the recommended option on rust-lang.org](https://www.rust-lang.org/tools/install) (rustup).
+
+Then, install [Homebrew](https://brew.sh/) if you don't already have it installed.
+
+Finally, install Node and Yarn.
+
+```
+brew install node yarn
+```
+
+### Get the source code
```
git clone https://github.com/LemmyNet/lemmy.git
# or alternatively from gitea
@@ -20,36 +30,49 @@ git clone https://github.com/LemmyNet/lemmy.git
All the following commands need to be run either in `lemmy/server` or `lemmy/ui`, as indicated
by the `cd` command.
-#### Build the backend (Rust)
+### Build the backend (Rust)
```
cd server
cargo build
# for development, use `cargo check` instead)
```
-#### Build the frontend (Typescript)
+### Build the frontend (Typescript)
```
cd ui
yarn
yarn build
```
-#### Setup postgresql
+### Setup postgresql
+#### Ubuntu
```
sudo apt install postgresql
sudo systemctl start postgresql
-# initialize postgres database
+
+# Either execute server/db-init.sh, or manually initialize the postgres database:
sudo -u postgres psql -c "create user lemmy with password 'password' superuser;" -U postgres
sudo -u postgres psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
-# or execute server/db-init.sh
```
-#### Run a local development instance
+#### macOS
+```
+brew install postgresql
+brew services start postgresql
+/usr/local/opt/postgres/bin/createuser -s postgres
+
+# Either execute server/db-init.sh, or manually initialize the postgres database:
+psql -c "create user lemmy with password 'password' superuser;" -U postgres
+psql -c 'create database lemmy with owner lemmy;' -U postgres
+export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
+```
+
+### Run a local development instance
```
# run each of these in a seperate terminal
cd server && cargo run
-ui & yarn start
+cd ui && yarn start
```
Then open [localhost:4444](http://localhost:4444) in your browser. It will auto-refresh if you edit
diff --git a/docs/src/contributing_websocket_http_api.md b/docs/src/contributing_websocket_http_api.md
index 6ed25b98e..62cb1fc44 100644
--- a/docs/src/contributing_websocket_http_api.md
+++ b/docs/src/contributing_websocket_http_api.md
@@ -17,6 +17,7 @@
- [Errors](#errors)
- [API documentation](#api-documentation)
* [Sort Types](#sort-types)
+ * [Undoing actions](#undoing-actions)
* [Websocket vs HTTP](#websocket-vs-http)
* [User / Authentication / Admin actions](#user--authentication--admin-actions)
+ [Login](#login)
@@ -43,142 +44,198 @@
- [Request](#request-5)
- [Response](#response-5)
- [HTTP](#http-6)
- + [Edit User Mention](#edit-user-mention)
+ + [Mark User Mention as read](#mark-user-mention-as-read)
- [Request](#request-6)
- [Response](#response-6)
- [HTTP](#http-7)
- + [Mark All As Read](#mark-all-as-read)
+ + [Get Private Messages](#get-private-messages)
- [Request](#request-7)
- [Response](#response-7)
- [HTTP](#http-8)
- + [Delete Account](#delete-account)
+ + [Create Private Message](#create-private-message)
- [Request](#request-8)
- [Response](#response-8)
- [HTTP](#http-9)
- + [Add admin](#add-admin)
+ + [Edit Private Message](#edit-private-message)
- [Request](#request-9)
- [Response](#response-9)
- [HTTP](#http-10)
- + [Ban user](#ban-user)
+ + [Delete Private Message](#delete-private-message)
- [Request](#request-10)
- [Response](#response-10)
- [HTTP](#http-11)
- * [Site](#site)
- + [List Categories](#list-categories)
+ + [Mark Private Message as Read](#mark-private-message-as-read)
- [Request](#request-11)
- [Response](#response-11)
- [HTTP](#http-12)
- + [Search](#search)
+ + [Mark All As Read](#mark-all-as-read)
- [Request](#request-12)
- [Response](#response-12)
- [HTTP](#http-13)
- + [Get Modlog](#get-modlog)
+ + [Delete Account](#delete-account)
- [Request](#request-13)
- [Response](#response-13)
- [HTTP](#http-14)
- + [Create Site](#create-site)
+ + [Add admin](#add-admin)
- [Request](#request-14)
- [Response](#response-14)
- [HTTP](#http-15)
- + [Edit Site](#edit-site)
+ + [Ban user](#ban-user)
- [Request](#request-15)
- [Response](#response-15)
- [HTTP](#http-16)
- + [Get Site](#get-site)
+ * [Site](#site)
+ + [List Categories](#list-categories)
- [Request](#request-16)
- [Response](#response-16)
- [HTTP](#http-17)
- + [Transfer Site](#transfer-site)
+ + [Search](#search)
- [Request](#request-17)
- [Response](#response-17)
- [HTTP](#http-18)
- + [Get Site Config](#get-site-config)
+ + [Get Modlog](#get-modlog)
- [Request](#request-18)
- [Response](#response-18)
- [HTTP](#http-19)
- + [Save Site Config](#save-site-config)
+ + [Create Site](#create-site)
- [Request](#request-19)
- [Response](#response-19)
- [HTTP](#http-20)
- * [Community](#community)
- + [Get Community](#get-community)
+ + [Edit Site](#edit-site)
- [Request](#request-20)
- [Response](#response-20)
- [HTTP](#http-21)
- + [Create Community](#create-community)
+ + [Get Site](#get-site)
- [Request](#request-21)
- [Response](#response-21)
- [HTTP](#http-22)
- + [List Communities](#list-communities)
+ + [Transfer Site](#transfer-site)
- [Request](#request-22)
- [Response](#response-22)
- [HTTP](#http-23)
- + [Ban from Community](#ban-from-community)
+ + [Get Site Config](#get-site-config)
- [Request](#request-23)
- [Response](#response-23)
- [HTTP](#http-24)
- + [Add Mod to Community](#add-mod-to-community)
+ + [Save Site Config](#save-site-config)
- [Request](#request-24)
- [Response](#response-24)
- [HTTP](#http-25)
- + [Edit Community](#edit-community)
+ * [Community](#community)
+ + [Get Community](#get-community)
- [Request](#request-25)
- [Response](#response-25)
- [HTTP](#http-26)
- + [Follow Community](#follow-community)
+ + [Create Community](#create-community)
- [Request](#request-26)
- [Response](#response-26)
- [HTTP](#http-27)
- + [Get Followed Communities](#get-followed-communities)
+ + [List Communities](#list-communities)
- [Request](#request-27)
- [Response](#response-27)
- [HTTP](#http-28)
- + [Transfer Community](#transfer-community)
+ + [Ban from Community](#ban-from-community)
- [Request](#request-28)
- [Response](#response-28)
- [HTTP](#http-29)
- * [Post](#post)
- + [Create Post](#create-post)
+ + [Add Mod to Community](#add-mod-to-community)
- [Request](#request-29)
- [Response](#response-29)
- [HTTP](#http-30)
- + [Get Post](#get-post)
+ + [Edit Community](#edit-community)
- [Request](#request-30)
- [Response](#response-30)
- [HTTP](#http-31)
- + [Get Posts](#get-posts)
+ + [Delete Community](#delete-community)
- [Request](#request-31)
- [Response](#response-31)
- [HTTP](#http-32)
- + [Create Post Like](#create-post-like)
+ + [Remove Community](#remove-community)
- [Request](#request-32)
- [Response](#response-32)
- [HTTP](#http-33)
- + [Edit Post](#edit-post)
+ + [Follow Community](#follow-community)
- [Request](#request-33)
- [Response](#response-33)
- [HTTP](#http-34)
- + [Save Post](#save-post)
+ + [Get Followed Communities](#get-followed-communities)
- [Request](#request-34)
- [Response](#response-34)
- [HTTP](#http-35)
- * [Comment](#comment)
- + [Create Comment](#create-comment)
+ + [Transfer Community](#transfer-community)
- [Request](#request-35)
- [Response](#response-35)
- [HTTP](#http-36)
- + [Edit Comment](#edit-comment)
+ * [Post](#post)
+ + [Create Post](#create-post)
- [Request](#request-36)
- [Response](#response-36)
- [HTTP](#http-37)
- + [Save Comment](#save-comment)
+ + [Get Post](#get-post)
- [Request](#request-37)
- [Response](#response-37)
- [HTTP](#http-38)
- + [Create Comment Like](#create-comment-like)
+ + [Get Posts](#get-posts)
- [Request](#request-38)
- [Response](#response-38)
- [HTTP](#http-39)
+ + [Create Post Like](#create-post-like)
+ - [Request](#request-39)
+ - [Response](#response-39)
+ - [HTTP](#http-40)
+ + [Edit Post](#edit-post)
+ - [Request](#request-40)
+ - [Response](#response-40)
+ - [HTTP](#http-41)
+ + [Delete Post](#delete-post)
+ - [Request](#request-41)
+ - [Response](#response-41)
+ - [HTTP](#http-42)
+ + [Remove Post](#remove-post)
+ - [Request](#request-42)
+ - [Response](#response-42)
+ - [HTTP](#http-43)
+ + [Lock Post](#lock-post)
+ - [Request](#request-43)
+ - [Response](#response-43)
+ - [HTTP](#http-44)
+ + [Sticky Post](#sticky-post)
+ - [Request](#request-44)
+ - [Response](#response-44)
+ - [HTTP](#http-45)
+ + [Save Post](#save-post)
+ - [Request](#request-45)
+ - [Response](#response-45)
+ - [HTTP](#http-46)
+ * [Comment](#comment)
+ + [Create Comment](#create-comment)
+ - [Request](#request-46)
+ - [Response](#response-46)
+ - [HTTP](#http-47)
+ + [Edit Comment](#edit-comment)
+ - [Request](#request-47)
+ - [Response](#response-47)
+ - [HTTP](#http-48)
+ + [Delete Comment](#delete-comment)
+ - [Request](#request-48)
+ - [Response](#response-48)
+ - [HTTP](#http-49)
+ + [Remove Comment](#remove-comment)
+ - [Request](#request-49)
+ - [Response](#response-49)
+ - [HTTP](#http-50)
+ + [Mark Comment as Read](#mark-comment-as-read)
+ - [Request](#request-50)
+ - [Response](#response-50)
+ - [HTTP](#http-51)
+ + [Save Comment](#save-comment)
+ - [Request](#request-51)
+ - [Response](#response-51)
+ - [HTTP](#http-52)
+ + [Create Comment Like](#create-comment-like)
+ - [Request](#request-52)
+ - [Response](#response-52)
+ - [HTTP](#http-53)
* [RSS / Atom feeds](#rss--atom-feeds)
+ [All](#all)
+ [Community](#community-1)
@@ -281,6 +338,10 @@ These go wherever there is a `sort` field. The available sort types are:
- `TopYear` - the most upvoted posts/communities of the current year.
- `TopAll` - the most upvoted posts/communities on the current instance.
+### Undoing actions
+
+Whenever you see a `deleted: bool`, `removed: bool`, `read: bool`, `locked: bool`, etc, you can undo this action by sending `false`.
+
### Websocket vs HTTP
- Below are the websocket JSON requests / responses. For HTTP, ignore all fields except those inside `data`.
@@ -464,14 +525,17 @@ Only the first user will be able to be the admin.
`GET /user/mentions`
-#### Edit User Mention
+#### Mark User Mention as read
+
+Only the recipient can do this.
+
##### Request
```rust
{
- op: "EditUserMention",
+ op: "MarkUserMentionAsRead",
data: {
user_mention_id: i32,
- read: Option
,
+ read: bool,
auth: String,
}
}
@@ -479,7 +543,7 @@ Only the first user will be able to be the admin.
##### Response
```rust
{
- op: "EditUserMention",
+ op: "MarkUserMentionAsRead",
data: {
mention: UserMentionView,
}
@@ -487,7 +551,141 @@ Only the first user will be able to be the admin.
```
##### HTTP
-`PUT /user/mention`
+`POST /user/mention/mark_as_read`
+
+#### Get Private Messages
+##### Request
+```rust
+{
+ op: "GetPrivateMessages",
+ data: {
+ unread_only: bool,
+ page: Option,
+ limit: Option,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "GetPrivateMessages",
+ data: {
+ messages: Vec,
+ }
+}
+```
+
+##### HTTP
+
+`GET /private_message/list`
+
+#### Create Private Message
+##### Request
+```rust
+{
+ op: "CreatePrivateMessage",
+ data: {
+ content: String,
+ recipient_id: i32,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "CreatePrivateMessage",
+ data: {
+ message: PrivateMessageView,
+ }
+}
+```
+
+##### HTTP
+
+`POST /private_message`
+
+#### Edit Private Message
+##### Request
+```rust
+{
+ op: "EditPrivateMessage",
+ data: {
+ edit_id: i32,
+ content: String,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "EditPrivateMessage",
+ data: {
+ message: PrivateMessageView,
+ }
+}
+```
+
+##### HTTP
+
+`PUT /private_message`
+
+#### Delete Private Message
+##### Request
+```rust
+{
+ op: "DeletePrivateMessage",
+ data: {
+ edit_id: i32,
+ deleted: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "DeletePrivateMessage",
+ data: {
+ message: PrivateMessageView,
+ }
+}
+```
+
+##### HTTP
+
+`POST /private_message/delete`
+
+#### Mark Private Message as Read
+
+Only the recipient can do this.
+
+##### Request
+```rust
+{
+ op: "MarkPrivateMessageAsRead",
+ data: {
+ edit_id: i32,
+ read: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "MarkPrivateMessageAsRead",
+ data: {
+ message: PrivateMessageView,
+ }
+}
+```
+
+##### HTTP
+
+`POST /private_message/mark_as_read`
#### Mark All As Read
@@ -754,6 +952,8 @@ Search types are `All, Comments, Posts, Communities, Users, Url`
site: Option,
admins: Vec,
banned: Vec,
+ online: usize, // This is currently broken
+ version: String,
}
}
```
@@ -854,7 +1054,6 @@ Search types are `All, Comments, Posts, Communities, Users, Url`
data: {
community: CommunityView,
moderators: Vec,
- admins: Vec,
}
}
```
@@ -971,7 +1170,7 @@ Search types are `All, Comments, Posts, Communities, Users, Url`
`POST /community/mod`
#### Edit Community
-Mods and admins can remove and lock a community, creators can delete it.
+Only mods can edit a community.
##### Request
```rust
@@ -979,14 +1178,9 @@ Mods and admins can remove and lock a community, creators can delete it.
op: "EditCommunity",
data: {
edit_id: i32,
- name: String,
title: String,
description: Option,
category_id: i32,
- removed: Option,
- deleted: Option,
- reason: Option,
- expires: Option,
auth: String
}
}
@@ -1004,6 +1198,62 @@ Mods and admins can remove and lock a community, creators can delete it.
`PUT /community`
+#### Delete Community
+Only a creator can delete a community
+
+##### Request
+```rust
+{
+ op: "DeleteCommunity",
+ data: {
+ edit_id: i32,
+ deleted: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "DeleteCommunity",
+ data: {
+ community: CommunityView
+ }
+}
+```
+##### HTTP
+
+`POST /community/delete`
+
+#### Remove Community
+Only admins can remove a community.
+
+##### Request
+```rust
+{
+ op: "RemoveCommunity",
+ data: {
+ edit_id: i32,
+ removed: bool,
+ reason: Option,
+ expires: Option,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "RemoveCommunity",
+ data: {
+ community: CommunityView
+ }
+}
+```
+##### HTTP
+
+`POST /community/remove`
+
#### Follow Community
##### Request
```rust
@@ -1089,8 +1339,9 @@ Mods and admins can remove and lock a community, creators can delete it.
name: String,
url: Option,
body: Option,
+ nsfw: bool,
community_id: i32,
- auth: String
+ auth: String,
}
}
```
@@ -1127,7 +1378,6 @@ Mods and admins can remove and lock a community, creators can delete it.
comments: Vec,
community: CommunityView,
moderators: Vec,
- admins: Vec,
}
}
```
@@ -1196,25 +1446,17 @@ Post listing types are `All, Subscribed, Community`
`POST /post/like`
#### Edit Post
-
-Mods and admins can remove and lock a post, creators can delete it.
-
##### Request
```rust
{
op: "EditPost",
data: {
edit_id: i32,
- creator_id: i32,
- community_id: i32,
name: String,
url: Option,
body: Option,
- removed: Option,
- deleted: Option,
- locked: Option,
- reason: Option,
- auth: String
+ nsfw: bool,
+ auth: String,
}
}
```
@@ -1232,6 +1474,120 @@ Mods and admins can remove and lock a post, creators can delete it.
`PUT /post`
+#### Delete Post
+##### Request
+```rust
+{
+ op: "DeletePost",
+ data: {
+ edit_id: i32,
+ deleted: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "DeletePost",
+ data: {
+ post: PostView
+ }
+}
+```
+
+##### HTTP
+
+`POST /post/delete`
+
+#### Remove Post
+
+Only admins and mods can remove a post.
+
+##### Request
+```rust
+{
+ op: "RemovePost",
+ data: {
+ edit_id: i32,
+ removed: bool,
+ reason: Option,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "RemovePost",
+ data: {
+ post: PostView
+ }
+}
+```
+
+##### HTTP
+
+`POST /post/remove`
+
+#### Lock Post
+
+Only admins and mods can lock a post.
+
+##### Request
+```rust
+{
+ op: "LockPost",
+ data: {
+ edit_id: i32,
+ locked: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "LockPost",
+ data: {
+ post: PostView
+ }
+}
+```
+
+##### HTTP
+
+`POST /post/lock`
+
+#### Sticky Post
+
+Only admins and mods can sticky a post.
+
+##### Request
+```rust
+{
+ op: "StickyPost",
+ data: {
+ edit_id: i32,
+ stickied: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "StickyPost",
+ data: {
+ post: PostView
+ }
+}
+```
+
+##### HTTP
+
+`POST /post/sticky`
+
#### Save Post
##### Request
```rust
@@ -1266,8 +1622,8 @@ Mods and admins can remove and lock a post, creators can delete it.
data: {
content: String,
parent_id: Option,
- edit_id: Option,
post_id: i32,
+ form_id: Option, // An optional form id, so you know which message came back
auth: String
}
}
@@ -1288,7 +1644,7 @@ Mods and admins can remove and lock a post, creators can delete it.
#### Edit Comment
-Mods and admins can remove a comment, creators can delete it.
+Only the creator can edit the comment.
##### Request
```rust
@@ -1296,15 +1652,9 @@ Mods and admins can remove a comment, creators can delete it.
op: "EditComment",
data: {
content: String,
- parent_id: Option,
edit_id: i32,
- creator_id: i32,
- post_id: i32,
- removed: Option,
- deleted: Option,
- reason: Option,
- read: Option,
- auth: String
+ form_id: Option,
+ auth: String,
}
}
```
@@ -1321,6 +1671,92 @@ Mods and admins can remove a comment, creators can delete it.
`PUT /comment`
+#### Delete Comment
+
+Only the creator can delete the comment.
+
+##### Request
+```rust
+{
+ op: "DeleteComment",
+ data: {
+ edit_id: i32,
+ deleted: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "DeleteComment",
+ data: {
+ comment: CommentView
+ }
+}
+```
+##### HTTP
+
+`POST /comment/delete`
+
+
+#### Remove Comment
+
+Only a mod or admin can remove the comment.
+
+##### Request
+```rust
+{
+ op: "RemoveComment",
+ data: {
+ edit_id: i32,
+ removed: bool,
+ reason: Option,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "RemoveComment",
+ data: {
+ comment: CommentView
+ }
+}
+```
+##### HTTP
+
+`POST /comment/remove`
+
+#### Mark Comment as Read
+
+Only the recipient can do this.
+
+##### Request
+```rust
+{
+ op: "MarkCommentAsRead",
+ data: {
+ edit_id: i32,
+ read: bool,
+ auth: String,
+ }
+}
+```
+##### Response
+```rust
+{
+ op: "MarkCommentAsRead",
+ data: {
+ comment: CommentView
+ }
+}
+```
+##### HTTP
+
+`POST /comment/mark_as_read`
+
#### Save Comment
##### Request
```rust
@@ -1356,7 +1792,6 @@ Mods and admins can remove a comment, creators can delete it.
op: "CreateCommentLike",
data: {
comment_id: i32,
- post_id: i32,
score: i16,
auth: String
}
diff --git a/server/Cargo.lock b/server/Cargo.lock
index d90b96799..a6171cf50 100644
--- a/server/Cargo.lock
+++ b/server/Cargo.lock
@@ -1,35 +1,9 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-[[package]]
-name = "activitystreams"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "464cb473bfb402b857cc15b1153974c203a43f1485da4dda15cd17a738548958"
-dependencies = [
- "activitystreams-derive",
- "chrono",
- "mime",
- "serde 1.0.114",
- "serde_json",
- "thiserror",
- "url",
-]
-
-[[package]]
-name = "activitystreams-derive"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c39ba5929399e9f921055bac76dd8f47419fa5b6b6da1ac4c1e82b94ed0ac7b4"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "activitystreams-ext"
version = "0.1.0"
-source = "git+https://git.asonix.dog/asonix/activitystreams-ext#e5c97f4ea9f60e49bc7ff27fb0fb515d3190fd25"
+source = "git+https://yerbamate.dev/asonix/activitystreams-ext?branch=main#2799a4c606467a2f577e1f45f93c6828ec83cfdf"
dependencies = [
"activitystreams-new",
"serde 1.0.114",
@@ -39,12 +13,14 @@ dependencies = [
[[package]]
name = "activitystreams-new"
version = "0.1.0"
-source = "git+https://git.asonix.dog/asonix/activitystreams-sketch#99c7e9aa5596eda846a1ebd5978ca72d11d4c08a"
+source = "git+https://yerbamate.dev/asonix/activitystreams-new?branch=main#857d5167dfa13054dd0d21d3d54f8147eea0d546"
dependencies = [
- "activitystreams",
+ "chrono",
+ "mime",
"serde 1.0.114",
"serde_json",
- "typed-builder",
+ "thiserror",
+ "url",
]
[[package]]
@@ -111,9 +87,9 @@ dependencies = [
[[package]]
name = "actix-files"
-version = "0.3.0-alpha.1"
+version = "0.3.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b32e0fdd5998c2712549cbc39dff46c8754d55e3dd9f4d017d9e28de30cac6"
+checksum = "627f597ad98061816766201db8afc7444752992f2919b2e60f53a7fa27f01aed"
dependencies = [
"actix-http",
"actix-service",
@@ -132,9 +108,9 @@ dependencies = [
[[package]]
name = "actix-http"
-version = "2.0.0-alpha.4"
+version = "2.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd7ea0568480d199952a51de70271946da57c33cc0e8b83f54383e70958dff21"
+checksum = "33f501768e82e8548763b7f55309e2f8bcc7f9f4273c75b47af99ac2b2581f37"
dependencies = [
"actix-codec",
"actix-connect",
@@ -147,6 +123,7 @@ dependencies = [
"bitflags",
"brotli2",
"bytes",
+ "cookie",
"copyless",
"derive_more",
"either",
@@ -160,6 +137,7 @@ dependencies = [
"http",
"httparse",
"indexmap",
+ "itoa",
"language-tags",
"lazy_static",
"log",
@@ -171,7 +149,7 @@ dependencies = [
"serde 1.0.114",
"serde_json",
"serde_urlencoded",
- "sha-1",
+ "sha-1 0.9.1",
"slab",
"time 0.2.16",
]
@@ -260,16 +238,16 @@ dependencies = [
[[package]]
name = "actix-threadpool"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91164716d956745c79dcea5e66d2aa04506549958accefcede5368c70f2fd4ff"
+checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30"
dependencies = [
"derive_more",
"futures-channel",
"lazy_static",
"log",
"num_cpus",
- "parking_lot 0.10.2",
+ "parking_lot 0.11.0",
"threadpool",
]
@@ -313,9 +291,9 @@ dependencies = [
[[package]]
name = "actix-web"
-version = "3.0.0-alpha.3"
+version = "3.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bd6df56ec5f9a1a0d8335f156f36e1e8f76dbd736fa0cc0f6bc3a69be1e6124"
+checksum = "9125c29b7d9911bfdb4d0d4d8f1cf4fee4f21515cf2a405a423c30c245364297"
dependencies = [
"actix-codec",
"actix-http",
@@ -353,24 +331,25 @@ dependencies = [
[[package]]
name = "actix-web-actors"
-version = "3.0.0-alpha.1"
+version = "3.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b5efeb3907582f9c724ce27be093ab8aafabd97be828bc6750c0d467f5e1aa3"
+checksum = "55ef22b33c49a28dda61866d5573c5b8ceb080a099cd59e7371b78b48bbf1bc0"
dependencies = [
"actix",
"actix-codec",
"actix-http",
"actix-web",
"bytes",
- "futures",
+ "futures-channel",
+ "futures-core",
"pin-project",
]
[[package]]
name = "actix-web-codegen"
-version = "0.2.2"
+version = "0.3.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a71bf475cbe07281d0b3696abb48212db118e7e23219f13596ce865235ff5766"
+checksum = "df9679f5b1f4c819de08b63b0a61a131b2fdc30b367c2c208984fda8eaa07fa0"
dependencies = [
"proc-macro2",
"quote",
@@ -390,24 +369,18 @@ dependencies = [
[[package]]
name = "addr2line"
-version = "0.12.2"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "602d785912f476e480434627e8732e6766b760c045bbf897d9dfaa9f4fbd399c"
+checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
-version = "0.2.2"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccc9a9dd069569f212bc4330af9f17c4afb5e8ce185e83dbb14f1349dda18b10"
-
-[[package]]
-name = "adler32"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
+checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "aho-corasick"
@@ -481,9 +454,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "awc"
-version = "2.0.0-alpha.2"
+version = "2.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7038a9747cd5159b9f0550895eaf865c0143baa7e4eee834e9294d0a7e0e4be"
+checksum = "374057b508d4083208996be82141891c2e14c8885f45991b21c1621200ab6df3"
dependencies = [
"actix-codec",
"actix-http",
@@ -505,14 +478,14 @@ dependencies = [
[[package]]
name = "backtrace"
-version = "0.3.49"
+version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c"
+checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
dependencies = [
"addr2line",
"cfg-if",
"libc",
- "miniz_oxide 0.3.7",
+ "miniz_oxide",
"object",
"rustc-demangle",
]
@@ -590,7 +563,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
- "generic-array 0.14.2",
+ "generic-array 0.14.3",
]
[[package]]
@@ -599,7 +572,7 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa136449e765dc7faa244561ccae839c394048667929af599b5d931ebe7b7f10"
dependencies = [
- "generic-array 0.14.2",
+ "generic-array 0.14.3",
]
[[package]]
@@ -642,6 +615,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "buf-min"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6ae7069aad07c7cdefe6a22a671f00650728bd2331a4cc62e1e5d0becdf9ca4"
+dependencies = [
+ "bytes",
+]
+
[[package]]
name = "bufstream"
version = "0.1.4"
@@ -668,12 +650,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "bytes"
-version = "0.5.5"
+version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b"
-dependencies = [
- "loom",
-]
+checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
name = "bytestring"
@@ -686,9 +665,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.57"
+version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fde55d2a2bfaa4c9668bbc63f531fbdeee3ffe188f4662511ce2c22b3eedebe"
+checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
[[package]]
name = "cfg-if"
@@ -698,9 +677,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "chrono"
-version = "0.4.12"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0fee792e164f78f5fe0c296cc2eb3688a2ca2b70cdff33040922d298203f0c4"
+checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6"
dependencies = [
"num-integer",
"num-traits 0.2.12",
@@ -770,6 +749,16 @@ dependencies = [
"serde-hjson",
]
+[[package]]
+name = "cookie"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca761767cf3fa9068cc893ec8c247a22d0fd0535848e65640c0548bd1f8bbb36"
+dependencies = [
+ "percent-encoding",
+ "time 0.2.16",
+]
+
[[package]]
name = "copyless"
version = "0.1.5"
@@ -794,9 +783,9 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
[[package]]
name = "cpuid-bool"
-version = "0.1.0"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d375c433320f6c5057ae04a04376eef4d04ce2801448cf8863a78da99107be4"
+checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
[[package]]
name = "crc32fast"
@@ -809,12 +798,12 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
+checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
dependencies = [
+ "cfg-if",
"crossbeam-utils",
- "maybe-uninit",
]
[[package]]
@@ -950,7 +939,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
- "generic-array 0.14.2",
+ "generic-array 0.14.3",
]
[[package]]
@@ -1142,7 +1131,7 @@ dependencies = [
"cfg-if",
"crc32fast",
"libc",
- "miniz_oxide 0.4.0",
+ "miniz_oxide",
]
[[package]]
@@ -1292,19 +1281,6 @@ dependencies = [
"byteorder",
]
-[[package]]
-name = "generator"
-version = "0.6.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68"
-dependencies = [
- "cc",
- "libc",
- "log",
- "rustc_version",
- "winapi 0.3.9",
-]
-
[[package]]
name = "generic-array"
version = "0.12.3"
@@ -1316,9 +1292,9 @@ dependencies = [
[[package]]
name = "generic-array"
-version = "0.14.2"
+version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980"
+checksum = "60fb4bb6bba52f78a471264d9a3b7d026cc0af47b22cd2cffbc0b787ca003e63"
dependencies = [
"typenum",
"version_check 0.9.2",
@@ -1337,15 +1313,15 @@ dependencies = [
[[package]]
name = "gimli"
-version = "0.21.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
+checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "h2"
-version = "0.2.5"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff"
+checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53"
dependencies = [
"bytes",
"fnv",
@@ -1354,10 +1330,19 @@ dependencies = [
"futures-util",
"http",
"indexmap",
- "log",
"slab",
"tokio",
"tokio-util 0.3.1",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
+dependencies = [
+ "autocfg 1.0.0",
]
[[package]]
@@ -1371,9 +1356,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
-version = "0.1.14"
+version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
+checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
dependencies = [
"libc",
]
@@ -1472,18 +1457,19 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
+checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7"
dependencies = [
"autocfg 1.0.0",
+ "hashbrown",
]
[[package]]
name = "instant"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69da7ce1490173c2bf4d26bc8be429aaeeaf4cce6c4b970b7949651fa17655fe"
+checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485"
[[package]]
name = "iovec"
@@ -1523,9 +1509,9 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "js-sys"
-version = "0.3.41"
+version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4b9172132a62451e56142bff9afc91c8e4a4500aa5b847da36815b63bfda916"
+checksum = "52732a3d3ad72c58ad2dc70624f9c17b46ecd0943b9a4f1ee37c4c18c5d983e2"
dependencies = [
"wasm-bindgen",
]
@@ -1573,19 +1559,21 @@ dependencies = [
"bcrypt",
"chrono",
"diesel",
+ "lazy_static",
"log",
+ "regex",
"serde 1.0.114",
"serde_json",
"sha2",
"strum",
"strum_macros",
+ "url",
]
[[package]]
name = "lemmy_server"
version = "0.0.1"
dependencies = [
- "activitystreams",
"activitystreams-ext",
"activitystreams-new",
"actix",
@@ -1693,9 +1681,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.71"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
+checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
[[package]]
name = "linked-hash-map"
@@ -1724,33 +1712,22 @@ dependencies = [
[[package]]
name = "lock_api"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de302ce1fe7482db13738fbaf2e21cfb06a986b89c0bf38d88abf16681aada4e"
+checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
-version = "0.4.8"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
]
-[[package]]
-name = "loom"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7"
-dependencies = [
- "cfg-if",
- "generator",
- "scoped-tls",
-]
-
[[package]]
name = "lru-cache"
version = "0.1.2"
@@ -1778,12 +1755,6 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
-[[package]]
-name = "maybe-uninit"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
-
[[package]]
name = "memchr"
version = "2.3.3"
@@ -1827,15 +1798,6 @@ dependencies = [
"unicase",
]
-[[package]]
-name = "miniz_oxide"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
-dependencies = [
- "adler32",
-]
-
[[package]]
name = "miniz_oxide"
version = "0.4.0"
@@ -2060,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
dependencies = [
"instant",
- "lock_api 0.4.0",
+ "lock_api 0.4.1",
"parking_lot_core 0.8.0",
]
@@ -2150,7 +2112,7 @@ checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
- "sha-1",
+ "sha-1 0.8.2",
]
[[package]]
@@ -2187,9 +2149,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
-version = "0.3.17"
+version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
+checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
[[package]]
name = "ppv-lite86"
@@ -2220,9 +2182,9 @@ checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
[[package]]
name = "proc-macro2"
-version = "1.0.18"
+version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
+checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
dependencies = [
"unicode-xid",
]
@@ -2434,9 +2396,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.1.56"
+version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "regex"
@@ -2559,12 +2521,6 @@ dependencies = [
"parking_lot 0.11.0",
]
-[[package]]
-name = "scoped-tls"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
-
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -2703,6 +2659,19 @@ dependencies = [
"opaque-debug 0.2.3",
]
+[[package]]
+name = "sha-1"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpuid-bool",
+ "digest 0.9.0",
+ "opaque-debug 0.3.0",
+]
+
[[package]]
name = "sha1"
version = "0.6.0"
@@ -2734,9 +2703,9 @@ dependencies = [
[[package]]
name = "simple_asn1"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618"
+checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b"
dependencies = [
"chrono",
"num-bigint",
@@ -2751,9 +2720,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
-version = "1.4.0"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
+checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f"
[[package]]
name = "socket2"
@@ -2869,9 +2838,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.33"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
+checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
dependencies = [
"proc-macro2",
"quote",
@@ -3076,6 +3045,26 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "tracing"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e2a2de6b0d5cbb13fc21193a2296888eaab62b6044479aafb3c54c01c29fcd"
+dependencies = [
+ "cfg-if",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94ae75f0d28ae10786f3b1895c55fe72e79928fd5ccdebb5438c75e93fec178f"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "trust-dns-proto"
version = "0.19.5"
@@ -3132,17 +3121,6 @@ version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
-[[package]]
-name = "typed-builder"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85fc4459191c621a53ef6c6ca5642e6e0e5ccc61f3e5b8ad6b6ab5317f0200fb"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "typenum"
version = "1.12.0"
@@ -3251,18 +3229,19 @@ dependencies = [
[[package]]
name = "v_escape"
-version = "0.7.4"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "660b101c07b5d0863deb9e7fb3138777e858d6d2a79f9e6049a27d1cc77c6da6"
+checksum = "b66158ce426982197fd44266d68125fd4000f1d42f5ee33ef02b500b4b6b0024"
dependencies = [
+ "buf-min",
"v_escape_derive",
]
[[package]]
name = "v_escape_derive"
-version = "0.5.6"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2ca2a14bc3fc5b64d188b087a7d3a927df87b152e941ccfbc66672e20c467ae"
+checksum = "cae7cffca0b1f9af9b20610f6fdeee9ffcce61417b5ad186a5d482dc904e24cd"
dependencies = [
"nom 4.2.3",
"proc-macro2",
@@ -3272,9 +3251,9 @@ dependencies = [
[[package]]
name = "v_htmlescape"
-version = "0.4.5"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e33e939c0d8cf047514fb6ba7d5aac78bc56677a6938b2ee67000b91f2e97e41"
+checksum = "f5fd25529cb2f78527b5ee507bcfb357b26d057b5e480853c26d49a4ead5c629"
dependencies = [
"cfg-if",
"v_escape",
@@ -3312,9 +3291,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasm-bindgen"
-version = "0.2.64"
+version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2"
+checksum = "f3edbcc9536ab7eababcc6d2374a0b7bfe13a2b6d562c5e07f370456b1a8f33d"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -3322,9 +3301,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.64"
+version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df"
+checksum = "89ed2fb8c84bfad20ea66b26a3743f3e7ba8735a69fe7d95118c33ec8fc1244d"
dependencies = [
"bumpalo",
"lazy_static",
@@ -3337,9 +3316,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.64"
+version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8"
+checksum = "eb071268b031a64d92fc6cf691715ca5a40950694d8f683c5bb43db7c730929e"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3347,9 +3326,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.64"
+version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75"
+checksum = "cf592c807080719d1ff2f245a687cbadb3ed28b2077ed7084b47aba8b691f2c6"
dependencies = [
"proc-macro2",
"quote",
@@ -3360,15 +3339,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.64"
+version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae"
+checksum = "72b6c0220ded549d63860c78c38f3bcc558d1ca3f4efa74942c536ddbbb55e87"
[[package]]
name = "web-sys"
-version = "0.3.41"
+version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "863539788676619aac1a23e2df3655e96b32b0e05eb72ca34ba045ad573c625d"
+checksum = "8be2398f326b7ba09815d0b403095f34dd708579220d099caae89be0b32137b2"
dependencies = [
"js-sys",
"wasm-bindgen",
diff --git a/server/Cargo.toml b/server/Cargo.toml
index 2aa3c139b..356cbced5 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -18,9 +18,8 @@ lemmy_db = { path = "./lemmy_db" }
diesel = "1.4.4"
diesel_migrations = "1.4.0"
dotenv = "0.15.0"
-activitystreams = "0.6.2"
-activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch" }
-activitystreams-ext = { git = "https://git.asonix.dog/asonix/activitystreams-ext" }
+activitystreams-new = { git = "https://yerbamate.dev/asonix/activitystreams-new", branch = "main" }
+activitystreams-ext = { git = "https://yerbamate.dev/asonix/activitystreams-ext", branch = "main" }
bcrypt = "0.8.0"
chrono = { version = "0.4.7", features = ["serde"] }
serde_json = { version = "1.0.52", features = ["preserve_order"]}
diff --git a/server/lemmy_db/Cargo.toml b/server/lemmy_db/Cargo.toml
index d94cf5fc6..6d342c1e7 100644
--- a/server/lemmy_db/Cargo.toml
+++ b/server/lemmy_db/Cargo.toml
@@ -12,4 +12,7 @@ strum = "0.18.0"
strum_macros = "0.18.0"
log = "0.4.0"
sha2 = "0.9"
-bcrypt = "0.8.0"
\ No newline at end of file
+bcrypt = "0.8.0"
+url = { version = "2.1.1", features = ["serde"] }
+lazy_static = "1.3.0"
+regex = "1.3.5"
diff --git a/server/lemmy_db/src/activity.rs b/server/lemmy_db/src/activity.rs
index 83f85ca1e..557eb9e9e 100644
--- a/server/lemmy_db/src/activity.rs
+++ b/server/lemmy_db/src/activity.rs
@@ -117,7 +117,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_862362".into(),
bio: None,
local: true,
private_key: None,
diff --git a/server/lemmy_db/src/comment.rs b/server/lemmy_db/src/comment.rs
index 602070d51..de6904133 100644
--- a/server/lemmy_db/src/comment.rs
+++ b/server/lemmy_db/src/comment.rs
@@ -1,5 +1,6 @@
use super::{post::Post, *};
use crate::schema::{comment, comment_like, comment_saved};
+use url::{ParseError, Url};
// WITH RECURSIVE MyTree AS (
// SELECT * FROM comment WHERE parent_id IS NULL
@@ -42,6 +43,12 @@ pub struct CommentForm {
pub local: bool,
}
+impl CommentForm {
+ pub fn get_ap_id(&self) -> Result {
+ Url::parse(&self.ap_id)
+ }
+}
+
impl Crud for Comment {
fn read(conn: &PgConnection, comment_id: i32) -> Result {
use crate::schema::comment::dsl::*;
@@ -90,14 +97,6 @@ impl Comment {
comment.filter(ap_id.eq(object_id)).first::(conn)
}
- pub fn mark_as_read(conn: &PgConnection, comment_id: i32) -> Result {
- use crate::schema::comment::dsl::*;
-
- diesel::update(comment.find(comment_id))
- .set(read.eq(true))
- .get_result::(conn)
- }
-
pub fn permadelete(conn: &PgConnection, comment_id: i32) -> Result {
use crate::schema::comment::dsl::*;
@@ -109,6 +108,46 @@ impl Comment {
))
.get_result::(conn)
}
+
+ pub fn update_deleted(
+ conn: &PgConnection,
+ comment_id: i32,
+ new_deleted: bool,
+ ) -> Result {
+ use crate::schema::comment::dsl::*;
+ diesel::update(comment.find(comment_id))
+ .set(deleted.eq(new_deleted))
+ .get_result::(conn)
+ }
+
+ pub fn update_removed(
+ conn: &PgConnection,
+ comment_id: i32,
+ new_removed: bool,
+ ) -> Result {
+ use crate::schema::comment::dsl::*;
+ diesel::update(comment.find(comment_id))
+ .set(removed.eq(new_removed))
+ .get_result::(conn)
+ }
+
+ pub fn update_read(conn: &PgConnection, comment_id: i32, new_read: bool) -> Result {
+ use crate::schema::comment::dsl::*;
+ diesel::update(comment.find(comment_id))
+ .set(read.eq(new_read))
+ .get_result::(conn)
+ }
+
+ pub fn update_content(
+ conn: &PgConnection,
+ comment_id: i32,
+ new_content: &str,
+ ) -> Result {
+ use crate::schema::comment::dsl::*;
+ diesel::update(comment.find(comment_id))
+ .set((content.eq(new_content), updated.eq(naive_now())))
+ .get_result::(conn)
+ }
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)]
@@ -226,7 +265,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_283687".into(),
bio: None,
local: true,
private_key: None,
@@ -246,7 +285,7 @@ mod tests {
deleted: None,
updated: None,
nsfw: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_928738972".into(),
local: true,
private_key: None,
public_key: None,
diff --git a/server/lemmy_db/src/comment_view.rs b/server/lemmy_db/src/comment_view.rs
index 4af13c2d9..7f99ba4ad 100644
--- a/server/lemmy_db/src/comment_view.rs
+++ b/server/lemmy_db/src/comment_view.rs
@@ -498,7 +498,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_92873982".into(),
bio: None,
local: true,
private_key: None,
@@ -518,7 +518,7 @@ mod tests {
deleted: None,
updated: None,
nsfw: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_7625376".into(),
local: true,
private_key: None,
public_key: None,
diff --git a/server/lemmy_db/src/community.rs b/server/lemmy_db/src/community.rs
index 607520803..3a78d769d 100644
--- a/server/lemmy_db/src/community.rs
+++ b/server/lemmy_db/src/community.rs
@@ -1,4 +1,5 @@
use crate::{
+ naive_now,
schema::{community, community_follower, community_moderator, community_user_ban},
Bannable,
Crud,
@@ -29,7 +30,6 @@ pub struct Community {
pub last_refreshed_at: chrono::NaiveDateTime,
}
-// TODO add better delete, remove, lock actions here.
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize, Debug)]
#[table_name = "community"]
pub struct CommunityForm {
@@ -88,10 +88,10 @@ impl Community {
.first::(conn)
}
- pub fn read_from_actor_id(conn: &PgConnection, community_id: &str) -> Result {
+ pub fn read_from_actor_id(conn: &PgConnection, for_actor_id: &str) -> Result {
use crate::schema::community::dsl::*;
community
- .filter(actor_id.eq(community_id))
+ .filter(actor_id.eq(for_actor_id))
.first::(conn)
}
@@ -99,6 +99,60 @@ impl Community {
use crate::schema::community::dsl::*;
community.filter(local.eq(true)).load::(conn)
}
+
+ pub fn update_deleted(
+ conn: &PgConnection,
+ community_id: i32,
+ new_deleted: bool,
+ ) -> Result {
+ use crate::schema::community::dsl::*;
+ diesel::update(community.find(community_id))
+ .set(deleted.eq(new_deleted))
+ .get_result::(conn)
+ }
+
+ pub fn update_removed(
+ conn: &PgConnection,
+ community_id: i32,
+ new_removed: bool,
+ ) -> Result {
+ use crate::schema::community::dsl::*;
+ diesel::update(community.find(community_id))
+ .set(removed.eq(new_removed))
+ .get_result::(conn)
+ }
+
+ pub fn update_creator(
+ conn: &PgConnection,
+ community_id: i32,
+ new_creator_id: i32,
+ ) -> Result {
+ use crate::schema::community::dsl::*;
+ diesel::update(community.find(community_id))
+ .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
+ .get_result::(conn)
+ }
+
+ fn community_mods_and_admins(
+ conn: &PgConnection,
+ community_id: i32,
+ ) -> Result, Error> {
+ use crate::{community_view::CommunityModeratorView, user_view::UserView};
+ let mut mods_and_admins: Vec = Vec::new();
+ mods_and_admins.append(
+ &mut CommunityModeratorView::for_community(conn, community_id)
+ .map(|v| v.into_iter().map(|m| m.user_id).collect())?,
+ );
+ mods_and_admins
+ .append(&mut UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
+ Ok(mods_and_admins)
+ }
+
+ pub fn is_mod_or_admin(conn: &PgConnection, user_id: i32, community_id: i32) -> bool {
+ Self::community_mods_and_admins(conn, community_id)
+ .unwrap_or_default()
+ .contains(&user_id)
+ }
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
@@ -258,7 +312,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_8266238".into(),
bio: None,
local: true,
private_key: None,
@@ -278,7 +332,7 @@ mod tests {
removed: None,
deleted: None,
updated: None,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_7625376".into(),
local: true,
private_key: None,
public_key: None,
@@ -300,7 +354,7 @@ mod tests {
deleted: false,
published: inserted_community.published,
updated: None,
- actor_id: "http://fake.com".into(),
+ actor_id: inserted_community.actor_id.to_owned(),
local: true,
private_key: None,
public_key: None,
diff --git a/server/lemmy_db/src/community_view.rs b/server/lemmy_db/src/community_view.rs
index 5c6bd81a1..880c94559 100644
--- a/server/lemmy_db/src/community_view.rs
+++ b/server/lemmy_db/src/community_view.rs
@@ -295,18 +295,18 @@ pub struct CommunityModeratorView {
}
impl CommunityModeratorView {
- pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result, Error> {
+ pub fn for_community(conn: &PgConnection, for_community_id: i32) -> Result, Error> {
use super::community_view::community_moderator_view::dsl::*;
community_moderator_view
- .filter(community_id.eq(from_community_id))
+ .filter(community_id.eq(for_community_id))
.order_by(published)
.load::(conn)
}
- pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result, Error> {
+ pub fn for_user(conn: &PgConnection, for_user_id: i32) -> Result, Error> {
use super::community_view::community_moderator_view::dsl::*;
community_moderator_view
- .filter(user_id.eq(from_user_id))
+ .filter(user_id.eq(for_user_id))
.order_by(published)
.load::(conn)
}
diff --git a/server/lemmy_db/src/lib.rs b/server/lemmy_db/src/lib.rs
index 2eead841d..cca2994b8 100644
--- a/server/lemmy_db/src/lib.rs
+++ b/server/lemmy_db/src/lib.rs
@@ -2,9 +2,12 @@
pub extern crate diesel;
#[macro_use]
pub extern crate strum_macros;
+#[macro_use]
+pub extern crate lazy_static;
pub extern crate bcrypt;
pub extern crate chrono;
pub extern crate log;
+pub extern crate regex;
pub extern crate serde;
pub extern crate serde_json;
pub extern crate sha2;
@@ -12,6 +15,7 @@ pub extern crate strum;
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *};
+use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{env, env::VarError};
@@ -172,10 +176,19 @@ pub fn naive_now() -> NaiveDateTime {
chrono::prelude::Utc::now().naive_utc()
}
+pub fn is_email_regex(test: &str) -> bool {
+ EMAIL_REGEX.is_match(test)
+}
+
+lazy_static! {
+ static ref EMAIL_REGEX: Regex =
+ Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
+}
+
#[cfg(test)]
mod tests {
use super::fuzzy_search;
- use crate::get_database_url_from_env;
+ use crate::{get_database_url_from_env, is_email_regex};
use diesel::{Connection, PgConnection};
pub fn establish_unpooled_connection() -> PgConnection {
@@ -194,4 +207,10 @@ mod tests {
let test = "This is a fuzzy search";
assert_eq!(fuzzy_search(test), "%This%is%a%fuzzy%search%".to_string());
}
+
+ #[test]
+ fn test_email() {
+ assert!(is_email_regex("gush@gmail.com"));
+ assert!(!is_email_regex("nada_neutho"));
+ }
}
diff --git a/server/lemmy_db/src/moderator.rs b/server/lemmy_db/src/moderator.rs
index f5d33d967..0992197b1 100644
--- a/server/lemmy_db/src/moderator.rs
+++ b/server/lemmy_db/src/moderator.rs
@@ -470,7 +470,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_829398".into(),
bio: None,
local: true,
private_key: None,
@@ -497,7 +497,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_82982738".into(),
bio: None,
local: true,
private_key: None,
@@ -517,7 +517,7 @@ mod tests {
deleted: None,
updated: None,
nsfw: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_283687".into(),
local: true,
private_key: None,
public_key: None,
diff --git a/server/lemmy_db/src/password_reset_request.rs b/server/lemmy_db/src/password_reset_request.rs
index a2692add8..2529ba67a 100644
--- a/server/lemmy_db/src/password_reset_request.rs
+++ b/server/lemmy_db/src/password_reset_request.rs
@@ -105,7 +105,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_8292378".into(),
bio: None,
local: true,
private_key: None,
diff --git a/server/lemmy_db/src/post.rs b/server/lemmy_db/src/post.rs
index 1525a675f..d46677897 100644
--- a/server/lemmy_db/src/post.rs
+++ b/server/lemmy_db/src/post.rs
@@ -8,6 +8,7 @@ use crate::{
};
use diesel::{dsl::*, result::Error, *};
use serde::{Deserialize, Serialize};
+use url::{ParseError, Url};
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
#[table_name = "post"]
@@ -56,6 +57,12 @@ pub struct PostForm {
pub local: bool,
}
+impl PostForm {
+ pub fn get_ap_id(&self) -> Result {
+ Url::parse(&self.ap_id)
+ }
+}
+
impl Post {
pub fn read(conn: &PgConnection, post_id: i32) -> Result {
use crate::schema::post::dsl::*;
@@ -101,6 +108,50 @@ impl Post {
))
.get_result::(conn)
}
+
+ pub fn update_deleted(
+ conn: &PgConnection,
+ post_id: i32,
+ new_deleted: bool,
+ ) -> Result {
+ use crate::schema::post::dsl::*;
+ diesel::update(post.find(post_id))
+ .set(deleted.eq(new_deleted))
+ .get_result::(conn)
+ }
+
+ pub fn update_removed(
+ conn: &PgConnection,
+ post_id: i32,
+ new_removed: bool,
+ ) -> Result {
+ use crate::schema::post::dsl::*;
+ diesel::update(post.find(post_id))
+ .set(removed.eq(new_removed))
+ .get_result::(conn)
+ }
+
+ pub fn update_locked(conn: &PgConnection, post_id: i32, new_locked: bool) -> Result {
+ use crate::schema::post::dsl::*;
+ diesel::update(post.find(post_id))
+ .set(locked.eq(new_locked))
+ .get_result::(conn)
+ }
+
+ pub fn update_stickied(
+ conn: &PgConnection,
+ post_id: i32,
+ new_stickied: bool,
+ ) -> Result {
+ use crate::schema::post::dsl::*;
+ diesel::update(post.find(post_id))
+ .set(stickied.eq(new_stickied))
+ .get_result::(conn)
+ }
+
+ pub fn is_post_creator(user_id: i32, post_creator_id: i32) -> bool {
+ user_id == post_creator_id
+ }
}
impl Crud for Post {
@@ -272,7 +323,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_8292683678".into(),
bio: None,
local: true,
private_key: None,
@@ -292,7 +343,7 @@ mod tests {
deleted: None,
updated: None,
nsfw: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_8223262378".into(),
local: true,
private_key: None,
public_key: None,
diff --git a/server/lemmy_db/src/post_view.rs b/server/lemmy_db/src/post_view.rs
index 3e9f87376..ffc8afebd 100644
--- a/server/lemmy_db/src/post_view.rs
+++ b/server/lemmy_db/src/post_view.rs
@@ -415,7 +415,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_8282738268".into(),
bio: None,
local: true,
private_key: None,
@@ -435,7 +435,7 @@ mod tests {
deleted: None,
updated: None,
nsfw: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_2763".into(),
local: true,
private_key: None,
public_key: None,
diff --git a/server/lemmy_db/src/private_message.rs b/server/lemmy_db/src/private_message.rs
index 1c0b455f3..3486cf545 100644
--- a/server/lemmy_db/src/private_message.rs
+++ b/server/lemmy_db/src/private_message.rs
@@ -1,4 +1,4 @@
-use crate::{schema::private_message, Crud};
+use crate::{naive_now, schema::private_message, Crud};
use diesel::{dsl::*, result::Error, *};
use serde::{Deserialize, Serialize};
@@ -80,6 +80,50 @@ impl PrivateMessage {
.filter(ap_id.eq(object_id))
.first::(conn)
}
+
+ pub fn update_content(
+ conn: &PgConnection,
+ private_message_id: i32,
+ new_content: &str,
+ ) -> Result {
+ use crate::schema::private_message::dsl::*;
+ diesel::update(private_message.find(private_message_id))
+ .set((content.eq(new_content), updated.eq(naive_now())))
+ .get_result::(conn)
+ }
+
+ pub fn update_deleted(
+ conn: &PgConnection,
+ private_message_id: i32,
+ new_deleted: bool,
+ ) -> Result {
+ use crate::schema::private_message::dsl::*;
+ diesel::update(private_message.find(private_message_id))
+ .set(deleted.eq(new_deleted))
+ .get_result::(conn)
+ }
+
+ pub fn update_read(
+ conn: &PgConnection,
+ private_message_id: i32,
+ new_read: bool,
+ ) -> Result {
+ use crate::schema::private_message::dsl::*;
+ diesel::update(private_message.find(private_message_id))
+ .set(read.eq(new_read))
+ .get_result::(conn)
+ }
+
+ pub fn mark_all_as_read(conn: &PgConnection, for_recipient_id: i32) -> Result, Error> {
+ use crate::schema::private_message::dsl::*;
+ diesel::update(
+ private_message
+ .filter(recipient_id.eq(for_recipient_id))
+ .filter(read.eq(false)),
+ )
+ .set(read.eq(true))
+ .get_results::(conn)
+ }
}
#[cfg(test)]
@@ -113,7 +157,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_6723878".into(),
bio: None,
local: true,
private_key: None,
@@ -140,7 +184,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_287263876".into(),
bio: None,
local: true,
private_key: None,
@@ -180,6 +224,10 @@ mod tests {
let read_private_message = PrivateMessage::read(&conn, inserted_private_message.id).unwrap();
let updated_private_message =
PrivateMessage::update(&conn, inserted_private_message.id, &private_message_form).unwrap();
+ let deleted_private_message =
+ PrivateMessage::update_deleted(&conn, inserted_private_message.id, true).unwrap();
+ let marked_read_private_message =
+ PrivateMessage::update_read(&conn, inserted_private_message.id, true).unwrap();
let num_deleted = PrivateMessage::delete(&conn, inserted_private_message.id).unwrap();
User_::delete(&conn, inserted_creator.id).unwrap();
User_::delete(&conn, inserted_recipient.id).unwrap();
@@ -187,6 +235,8 @@ mod tests {
assert_eq!(expected_private_message, read_private_message);
assert_eq!(expected_private_message, updated_private_message);
assert_eq!(expected_private_message, inserted_private_message);
+ assert!(deleted_private_message.deleted);
+ assert!(marked_read_private_message.read);
assert_eq!(1, num_deleted);
}
}
diff --git a/server/lemmy_db/src/user.rs b/server/lemmy_db/src/user.rs
index 556fc1a75..e53890770 100644
--- a/server/lemmy_db/src/user.rs
+++ b/server/lemmy_db/src/user.rs
@@ -1,4 +1,5 @@
use crate::{
+ is_email_regex,
naive_now,
schema::{user_, user_::dsl::*},
Crud,
@@ -125,9 +126,18 @@ impl User_ {
use crate::schema::user_::dsl::*;
user_.filter(actor_id.eq(object_id)).first::(conn)
}
-}
-impl User_ {
+ pub fn find_by_email_or_username(
+ conn: &PgConnection,
+ username_or_email: &str,
+ ) -> Result {
+ if is_email_regex(username_or_email) {
+ Self::find_by_email(conn, username_or_email)
+ } else {
+ Self::find_by_username(conn, username_or_email)
+ }
+ }
+
pub fn find_by_username(conn: &PgConnection, username: &str) -> Result {
user_.filter(name.eq(username)).first::(conn)
}
@@ -166,7 +176,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_9826382637".into(),
bio: None,
local: true,
private_key: None,
@@ -195,7 +205,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: inserted_user.actor_id.to_owned(),
bio: None,
local: true,
private_key: None,
diff --git a/server/lemmy_db/src/user_mention.rs b/server/lemmy_db/src/user_mention.rs
index 9f23f4410..f32318e0a 100644
--- a/server/lemmy_db/src/user_mention.rs
+++ b/server/lemmy_db/src/user_mention.rs
@@ -52,6 +52,30 @@ impl Crud for UserMention {
}
}
+impl UserMention {
+ pub fn update_read(
+ conn: &PgConnection,
+ user_mention_id: i32,
+ new_read: bool,
+ ) -> Result {
+ use crate::schema::user_mention::dsl::*;
+ diesel::update(user_mention.find(user_mention_id))
+ .set(read.eq(new_read))
+ .get_result::(conn)
+ }
+
+ pub fn mark_all_as_read(conn: &PgConnection, for_recipient_id: i32) -> Result, Error> {
+ use crate::schema::user_mention::dsl::*;
+ diesel::update(
+ user_mention
+ .filter(recipient_id.eq(for_recipient_id))
+ .filter(read.eq(false)),
+ )
+ .set(read.eq(true))
+ .get_results::(conn)
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::{
@@ -86,7 +110,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_628763".into(),
bio: None,
local: true,
private_key: None,
@@ -113,7 +137,7 @@ mod tests {
lang: "browser".into(),
show_avatars: true,
send_notifications_to_email: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_927389278".into(),
bio: None,
local: true,
private_key: None,
@@ -133,7 +157,7 @@ mod tests {
deleted: None,
updated: None,
nsfw: false,
- actor_id: "http://fake.com".into(),
+ actor_id: "changeme_876238".into(),
local: true,
private_key: None,
public_key: None,
diff --git a/server/lemmy_db/src/user_view.rs b/server/lemmy_db/src/user_view.rs
index f2ac47422..d61fe9c52 100644
--- a/server/lemmy_db/src/user_view.rs
+++ b/server/lemmy_db/src/user_view.rs
@@ -157,7 +157,28 @@ impl UserView {
pub fn admins(conn: &PgConnection) -> Result, Error> {
use super::user_view::user_fast::dsl::*;
+ use diesel::sql_types::{Nullable, Text};
user_fast
+ // The select is necessary here to not get back emails
+ .select((
+ id,
+ actor_id,
+ name,
+ avatar,
+ "".into_sql::>(),
+ matrix_user_id,
+ bio,
+ local,
+ admin,
+ banned,
+ show_avatars,
+ send_notifications_to_email,
+ published,
+ number_of_posts,
+ post_score,
+ number_of_comments,
+ comment_score,
+ ))
.filter(admin.eq(true))
.order_by(published)
.load::(conn)
@@ -165,6 +186,28 @@ impl UserView {
pub fn banned(conn: &PgConnection) -> Result, Error> {
use super::user_view::user_fast::dsl::*;
- user_fast.filter(banned.eq(true)).load::(conn)
+ use diesel::sql_types::{Nullable, Text};
+ user_fast
+ .select((
+ id,
+ actor_id,
+ name,
+ avatar,
+ "".into_sql::>(),
+ matrix_user_id,
+ bio,
+ local,
+ admin,
+ banned,
+ show_avatars,
+ send_notifications_to_email,
+ published,
+ number_of_posts,
+ post_score,
+ number_of_comments,
+ comment_score,
+ ))
+ .filter(banned.eq(true))
+ .load::(conn)
}
}
diff --git a/server/lemmy_utils/Cargo.toml b/server/lemmy_utils/Cargo.toml
index fed22f585..9685c0eda 100644
--- a/server/lemmy_utils/Cargo.toml
+++ b/server/lemmy_utils/Cargo.toml
@@ -19,4 +19,4 @@ serde_json = { version = "1.0.52", features = ["preserve_order"]}
comrak = "0.7"
lazy_static = "1.3.0"
openssl = "0.10"
-url = { version = "2.1.1", features = ["serde"] }
\ No newline at end of file
+url = { version = "2.1.1", features = ["serde"] }
diff --git a/server/lemmy_utils/src/lib.rs b/server/lemmy_utils/src/lib.rs
index d88335e2d..6d851b033 100644
--- a/server/lemmy_utils/src/lib.rs
+++ b/server/lemmy_utils/src/lib.rs
@@ -44,10 +44,6 @@ pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime {
DateTime::::from_utc(datetime, *now.offset())
}
-pub fn is_email_regex(test: &str) -> bool {
- EMAIL_REGEX.is_match(test)
-}
-
pub fn remove_slurs(test: &str) -> String {
SLUR_REGEX.replace_all(test, "*removed*").to_string()
}
@@ -165,7 +161,6 @@ pub fn is_valid_post_title(title: &str) -> bool {
#[cfg(test)]
mod tests {
use crate::{
- is_email_regex,
is_valid_community_name,
is_valid_post_title,
is_valid_username,
@@ -185,12 +180,6 @@ mod tests {
assert_eq!(mentions[1].domain, "lemmy-alpha:8540".to_string());
}
- #[test]
- fn test_email() {
- assert!(is_email_regex("gush@gmail.com"));
- assert!(!is_email_regex("nada_neutho"));
- }
-
#[test]
fn test_valid_register_username() {
assert!(is_valid_username("Hello_98"));
diff --git a/server/migrations/2020-07-18-234519_add_unique_community_user_actor_ids/down.sql b/server/migrations/2020-07-18-234519_add_unique_community_user_actor_ids/down.sql
new file mode 100644
index 000000000..7a4f2e2f1
--- /dev/null
+++ b/server/migrations/2020-07-18-234519_add_unique_community_user_actor_ids/down.sql
@@ -0,0 +1,20 @@
+
+alter table community alter column actor_id set not null;
+alter table community alter column actor_id set default 'http://fake.com';
+alter table user_ alter column actor_id set not null;
+alter table user_ alter column actor_id set default 'http://fake.com';
+
+drop function generate_unique_changeme;
+
+update community
+set actor_id = 'http://fake.com'
+where actor_id like 'changeme_%';
+
+update user_
+set actor_id = 'http://fake.com'
+where actor_id like 'changeme_%';
+
+drop index idx_user_lower_actor_id;
+create unique index idx_user_name_lower_actor_id on user_ (lower(name), lower(actor_id));
+
+drop index idx_community_lower_actor_id;
diff --git a/server/migrations/2020-07-18-234519_add_unique_community_user_actor_ids/up.sql b/server/migrations/2020-07-18-234519_add_unique_community_user_actor_ids/up.sql
new file mode 100644
index 000000000..e32ed5e08
--- /dev/null
+++ b/server/migrations/2020-07-18-234519_add_unique_community_user_actor_ids/up.sql
@@ -0,0 +1,50 @@
+-- Following this issue : https://github.com/LemmyNet/lemmy/issues/957
+
+-- Creating a unique changeme actor_id
+create or replace function generate_unique_changeme()
+returns text language sql
+as $$
+ select 'changeme_' || string_agg (substr('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789', ceil (random() * 62)::integer, 1), '')
+ from generate_series(1, 20)
+$$;
+
+-- Need to delete the possible community and user dupes for ones that don't start with the fake one
+-- A few test inserts, to make sure this removes later dupes
+-- insert into community (name, title, category_id, creator_id) values ('testcom', 'another testcom', 1, 2);
+delete from community a using (
+ select min(id) as id, actor_id
+ from community
+ group by actor_id having count(*) > 1
+) b
+where a.actor_id = b.actor_id
+and a.id <> b.id;
+
+delete from user_ a using (
+ select min(id) as id, actor_id
+ from user_
+ group by actor_id having count(*) > 1
+) b
+where a.actor_id = b.actor_id
+and a.id <> b.id;
+
+-- Replacing the current default on the columns, to the unique one
+update community
+set actor_id = generate_unique_changeme()
+where actor_id = 'http://fake.com';
+
+update user_
+set actor_id = generate_unique_changeme()
+where actor_id = 'http://fake.com';
+
+-- Add the unique indexes
+alter table community alter column actor_id set not null;
+alter table community alter column actor_id set default generate_unique_changeme();
+
+alter table user_ alter column actor_id set not null;
+alter table user_ alter column actor_id set default generate_unique_changeme();
+
+-- Add lowercase uniqueness too
+drop index idx_user_name_lower_actor_id;
+create unique index idx_user_lower_actor_id on user_ (lower(actor_id));
+
+create unique index idx_community_lower_actor_id on community (lower(actor_id));
diff --git a/server/src/api/claims.rs b/server/src/api/claims.rs
index eec9d1a71..9118714ba 100644
--- a/server/src/api/claims.rs
+++ b/server/src/api/claims.rs
@@ -1,7 +1,7 @@
use diesel::{result::Error, PgConnection};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
use lemmy_db::{user::User_, Crud};
-use lemmy_utils::{is_email_regex, settings::Settings};
+use lemmy_utils::settings::Settings;
use serde::{Deserialize, Serialize};
type Jwt = String;
@@ -54,18 +54,6 @@ impl Claims {
.unwrap()
}
- // TODO: move these into user?
- pub fn find_by_email_or_username(
- conn: &PgConnection,
- username_or_email: &str,
- ) -> Result {
- if is_email_regex(username_or_email) {
- User_::find_by_email(conn, username_or_email)
- } else {
- User_::find_by_username(conn, username_or_email)
- }
- }
-
pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result {
let claims: Claims = Claims::decode(&jwt).expect("Invalid token").claims;
User_::read(&conn, claims.id)
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs
index f8bdf5d5b..df772f535 100644
--- a/server/src/api/comment.rs
+++ b/server/src/api/comment.rs
@@ -1,5 +1,5 @@
use crate::{
- api::{claims::Claims, APIError, Oper, Perform},
+ api::{claims::Claims, is_mod_or_admin, APIError, Oper, Perform},
apub::{ApubLikeableType, ApubObjectType},
blocking,
websocket::{
@@ -15,12 +15,10 @@ use lemmy_db::{
comment_view::*,
community_view::*,
moderator::*,
- naive_now,
post::*,
site_view::*,
user::*,
user_mention::*,
- user_view::*,
Crud,
Likeable,
ListingType,
@@ -44,22 +42,38 @@ use std::str::FromStr;
pub struct CreateComment {
content: String,
parent_id: Option,
- edit_id: Option, // TODO this isn't used
pub post_id: i32,
+ form_id: Option,
auth: String,
}
#[derive(Serialize, Deserialize)]
pub struct EditComment {
content: String,
- parent_id: Option, // TODO why are the parent_id, creator_id, post_id, etc fields required? They aren't going to change
edit_id: i32,
- creator_id: i32,
- pub post_id: i32,
- removed: Option,
- deleted: Option,
+ form_id: Option,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeleteComment {
+ edit_id: i32,
+ deleted: bool,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RemoveComment {
+ edit_id: i32,
+ removed: bool,
reason: Option,
- read: Option,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct MarkCommentAsRead {
+ edit_id: i32,
+ read: bool,
auth: String,
}
@@ -74,12 +88,12 @@ pub struct SaveComment {
pub struct CommentResponse {
pub comment: CommentView,
pub recipient_ids: Vec,
+ pub form_id: Option,
}
#[derive(Serialize, Deserialize)]
pub struct CreateCommentLike {
comment_id: i32,
- pub post_id: i32,
score: i16,
auth: String,
}
@@ -150,6 +164,12 @@ impl Perform for Oper {
return Err(APIError::err("site_ban").into());
}
+ // Check if post is locked, no new comments
+ if post.locked {
+ return Err(APIError::err("locked").into());
+ }
+
+ // Create the comment
let comment_form2 = comment_form.clone();
let inserted_comment =
match blocking(pool, move |conn| Comment::create(&conn, &comment_form2)).await? {
@@ -157,6 +177,7 @@ impl Perform for Oper {
Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
};
+ // Necessary to update the ap_id
let inserted_comment_id = inserted_comment.id;
let updated_comment: Comment = match blocking(pool, move |conn| {
let apub_id =
@@ -175,8 +196,15 @@ impl Perform for Oper {
// Scan the comment for user mentions, add those rows
let mentions = scrape_text_for_mentions(&comment_form.content);
- let recipient_ids =
- send_local_notifs(mentions, updated_comment.clone(), user.clone(), post, pool).await?;
+ let recipient_ids = send_local_notifs(
+ mentions,
+ updated_comment.clone(),
+ user.clone(),
+ post,
+ pool,
+ true,
+ )
+ .await?;
// You like your own comment by default
let like_form = CommentLikeForm {
@@ -201,6 +229,7 @@ impl Perform for Oper {
let mut res = CommentResponse {
comment: comment_view,
recipient_ids,
+ form_id: data.form_id.to_owned(),
};
if let Some(ws) = websocket_info {
@@ -237,122 +266,34 @@ impl Perform for Oper {
let user_id = claims.id;
- let user = blocking(pool, move |conn| User_::read(&conn, user_id)).await??;
-
let edit_id = data.edit_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
- let mut editors: Vec = vec![orig_comment.creator_id];
- let mut moderators: Vec = vec![];
-
- let community_id = orig_comment.community_id;
- moderators.append(
- &mut blocking(pool, move |conn| {
- CommunityModeratorView::for_community(&conn, community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- moderators.append(
- &mut blocking(pool, move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
-
- editors.extend(&moderators);
- // You are allowed to mark the comment as read even if you're banned.
- if data.read.is_none() {
- // Verify its the creator or a mod, or an admin
-
- if !editors.contains(&user_id) {
- return Err(APIError::err("no_comment_edit_allowed").into());
- }
-
- // Check for a community ban
- let community_id = orig_comment.community_id;
- let is_banned =
- move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
- if blocking(pool, is_banned).await? {
- return Err(APIError::err("community_ban").into());
- }
-
- // Check for a site ban
- if user.banned {
- return Err(APIError::err("site_ban").into());
- }
- } else {
- // check that user can mark as read
- let parent_id = orig_comment.parent_id;
- match parent_id {
- Some(pid) => {
- let parent_comment =
- blocking(pool, move |conn| CommentView::read(&conn, pid, None)).await??;
- if user_id != parent_comment.creator_id {
- return Err(APIError::err("no_comment_edit_allowed").into());
- }
- }
- None => {
- let parent_post_id = orig_comment.post_id;
- let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
- if user_id != parent_post.creator_id {
- return Err(APIError::err("no_comment_edit_allowed").into());
- }
- }
- }
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
}
+ // Check for a community ban
+ let community_id = orig_comment.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only the creator can edit
+ if user_id != orig_comment.creator_id {
+ return Err(APIError::err("no_comment_edit_allowed").into());
+ }
+
+ // Do the update
let content_slurs_removed = remove_slurs(&data.content.to_owned());
-
let edit_id = data.edit_id;
- let read_comment = blocking(pool, move |conn| Comment::read(conn, edit_id)).await??;
-
- let comment_form = {
- if data.read.is_none() {
- // the ban etc checks should been made and have passed
- // the comment can be properly edited
- let post_removed = if moderators.contains(&user_id) {
- data.removed
- } else {
- Some(read_comment.removed)
- };
-
- CommentForm {
- content: content_slurs_removed,
- parent_id: read_comment.parent_id,
- post_id: read_comment.post_id,
- creator_id: read_comment.creator_id,
- removed: post_removed.to_owned(),
- deleted: data.deleted.to_owned(),
- read: Some(read_comment.read),
- published: None,
- updated: Some(naive_now()),
- ap_id: read_comment.ap_id,
- local: read_comment.local,
- }
- } else {
- // the only field that can be updated it the read field
- CommentForm {
- content: read_comment.content,
- parent_id: read_comment.parent_id,
- post_id: read_comment.post_id,
- creator_id: read_comment.creator_id,
- removed: Some(read_comment.removed).to_owned(),
- deleted: Some(read_comment.deleted).to_owned(),
- read: data.read.to_owned(),
- published: None,
- updated: orig_comment.updated,
- ap_id: read_comment.ap_id,
- local: read_comment.local,
- }
- }
- };
-
- let edit_id = data.edit_id;
- let comment_form2 = comment_form.clone();
let updated_comment = match blocking(pool, move |conn| {
- Comment::update(conn, edit_id, &comment_form2)
+ Comment::update_content(conn, edit_id, &content_slurs_removed)
})
.await?
{
@@ -360,54 +301,19 @@ impl Perform for Oper {
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
};
- if data.read.is_none() {
- if let Some(deleted) = data.deleted.to_owned() {
- if deleted {
- updated_comment
- .send_delete(&user, &self.client, pool)
- .await?;
- } else {
- updated_comment
- .send_undo_delete(&user, &self.client, pool)
- .await?;
- }
- } else if let Some(removed) = data.removed.to_owned() {
- if moderators.contains(&user_id) {
- if removed {
- updated_comment
- .send_remove(&user, &self.client, pool)
- .await?;
- } else {
- updated_comment
- .send_undo_remove(&user, &self.client, pool)
- .await?;
- }
- }
- } else {
- updated_comment
- .send_update(&user, &self.client, pool)
- .await?;
- }
+ // Send the apub update
+ updated_comment
+ .send_update(&user, &self.client, pool)
+ .await?;
- // Mod tables
- if moderators.contains(&user_id) {
- if let Some(removed) = data.removed.to_owned() {
- let form = ModRemoveCommentForm {
- mod_user_id: user_id,
- comment_id: data.edit_id,
- removed: Some(removed),
- reason: data.reason.to_owned(),
- };
- blocking(pool, move |conn| ModRemoveComment::create(conn, &form)).await??;
- }
- }
- }
-
- let post_id = data.post_id;
+ // Do the mentions / recipients
+ let post_id = orig_comment.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
- let mentions = scrape_text_for_mentions(&comment_form.content);
- let recipient_ids = send_local_notifs(mentions, updated_comment, user, post, pool).await?;
+ let updated_comment_content = updated_comment.content.to_owned();
+ let mentions = scrape_text_for_mentions(&updated_comment_content);
+ let recipient_ids =
+ send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
let edit_id = data.edit_id;
let comment_view = blocking(pool, move |conn| {
@@ -418,6 +324,7 @@ impl Perform for Oper {
let mut res = CommentResponse {
comment: comment_view,
recipient_ids,
+ form_id: data.form_id.to_owned(),
};
if let Some(ws) = websocket_info {
@@ -436,6 +343,291 @@ impl Perform for Oper {
}
}
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = CommentResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &DeleteComment = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let edit_id = data.edit_id;
+ let orig_comment =
+ blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ // Check for a community ban
+ let community_id = orig_comment.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only the creator can delete
+ if user_id != orig_comment.creator_id {
+ return Err(APIError::err("no_comment_edit_allowed").into());
+ }
+
+ // Do the delete
+ let deleted = data.deleted;
+ let updated_comment = match blocking(pool, move |conn| {
+ Comment::update_deleted(conn, edit_id, deleted)
+ })
+ .await?
+ {
+ Ok(comment) => comment,
+ Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
+ };
+
+ // Send the apub message
+ if deleted {
+ updated_comment
+ .send_delete(&user, &self.client, pool)
+ .await?;
+ } else {
+ updated_comment
+ .send_undo_delete(&user, &self.client, pool)
+ .await?;
+ }
+
+ // Refetch it
+ let edit_id = data.edit_id;
+ let comment_view = blocking(pool, move |conn| {
+ CommentView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ // Build the recipients
+ let post_id = comment_view.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+ let mentions = vec![];
+ let recipient_ids =
+ send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
+
+ let mut res = CommentResponse {
+ comment: comment_view,
+ recipient_ids,
+ form_id: None,
+ };
+
+ if let Some(ws) = websocket_info {
+ ws.chatserver.do_send(SendComment {
+ op: UserOperation::DeleteComment,
+ comment: res.clone(),
+ my_id: ws.id,
+ });
+
+ // strip out the recipient_ids, so that
+ // users don't get double notifs
+ res.recipient_ids = Vec::new();
+ }
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = CommentResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &RemoveComment = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let edit_id = data.edit_id;
+ let orig_comment =
+ blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ // Check for a community ban
+ let community_id = orig_comment.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only a mod or admin can remove
+ is_mod_or_admin(pool, user_id, community_id).await?;
+
+ // Do the remove
+ let removed = data.removed;
+ let updated_comment = match blocking(pool, move |conn| {
+ Comment::update_removed(conn, edit_id, removed)
+ })
+ .await?
+ {
+ Ok(comment) => comment,
+ Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
+ };
+
+ // Mod tables
+ let form = ModRemoveCommentForm {
+ mod_user_id: user_id,
+ comment_id: data.edit_id,
+ removed: Some(removed),
+ reason: data.reason.to_owned(),
+ };
+ blocking(pool, move |conn| ModRemoveComment::create(conn, &form)).await??;
+
+ // Send the apub message
+ if removed {
+ updated_comment
+ .send_remove(&user, &self.client, pool)
+ .await?;
+ } else {
+ updated_comment
+ .send_undo_remove(&user, &self.client, pool)
+ .await?;
+ }
+
+ // Refetch it
+ let edit_id = data.edit_id;
+ let comment_view = blocking(pool, move |conn| {
+ CommentView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ // Build the recipients
+ let post_id = comment_view.post_id;
+ let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
+ let mentions = vec![];
+ let recipient_ids =
+ send_local_notifs(mentions, updated_comment, user, post, pool, false).await?;
+
+ let mut res = CommentResponse {
+ comment: comment_view,
+ recipient_ids,
+ form_id: None,
+ };
+
+ if let Some(ws) = websocket_info {
+ ws.chatserver.do_send(SendComment {
+ op: UserOperation::RemoveComment,
+ comment: res.clone(),
+ my_id: ws.id,
+ });
+
+ // strip out the recipient_ids, so that
+ // users don't get double notifs
+ res.recipient_ids = Vec::new();
+ }
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = CommentResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ _websocket_info: Option,
+ ) -> Result {
+ let data: &MarkCommentAsRead = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let edit_id = data.edit_id;
+ let orig_comment =
+ blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ // Check for a community ban
+ let community_id = orig_comment.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only the recipient can mark as read
+ // Needs to fetch the parent comment / post to get the recipient
+ let parent_id = orig_comment.parent_id;
+ match parent_id {
+ Some(pid) => {
+ let parent_comment =
+ blocking(pool, move |conn| CommentView::read(&conn, pid, None)).await??;
+ if user_id != parent_comment.creator_id {
+ return Err(APIError::err("no_comment_edit_allowed").into());
+ }
+ }
+ None => {
+ let parent_post_id = orig_comment.post_id;
+ let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
+ if user_id != parent_post.creator_id {
+ return Err(APIError::err("no_comment_edit_allowed").into());
+ }
+ }
+ }
+
+ // Do the mark as read
+ let read = data.read;
+ match blocking(pool, move |conn| Comment::update_read(conn, edit_id, read)).await? {
+ Ok(comment) => comment,
+ Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
+ };
+
+ // Refetch it
+ let edit_id = data.edit_id;
+ let comment_view = blocking(pool, move |conn| {
+ CommentView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ let res = CommentResponse {
+ comment: comment_view,
+ recipient_ids: Vec::new(),
+ form_id: None,
+ };
+
+ Ok(res)
+ }
+}
+
#[async_trait::async_trait(?Send)]
impl Perform for Oper {
type Response = CommentResponse;
@@ -480,6 +672,7 @@ impl Perform for Oper {
Ok(CommentResponse {
comment: comment_view,
recipient_ids: Vec::new(),
+ form_id: None,
})
}
}
@@ -512,8 +705,12 @@ impl Perform for Oper {
}
}
+ let comment_id = data.comment_id;
+ let orig_comment =
+ blocking(pool, move |conn| CommentView::read(&conn, comment_id, None)).await??;
+
// Check for a community ban
- let post_id = data.post_id;
+ let post_id = orig_comment.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let community_id = post.community_id;
let is_banned =
@@ -550,7 +747,7 @@ impl Perform for Oper {
let like_form = CommentLikeForm {
comment_id: data.comment_id,
- post_id: data.post_id,
+ post_id,
user_id,
score: data.score,
};
@@ -587,6 +784,7 @@ impl Perform for Oper {
let mut res = CommentResponse {
comment: liked_comment,
recipient_ids,
+ form_id: None,
};
if let Some(ws) = websocket_info {
@@ -675,9 +873,10 @@ pub async fn send_local_notifs(
user: User_,
post: Post,
pool: &DbPool,
+ do_send_email: bool,
) -> Result, LemmyError> {
let ids = blocking(pool, move |conn| {
- do_send_local_notifs(conn, &mentions, &comment, &user, &post)
+ do_send_local_notifs(conn, &mentions, &comment, &user, &post, do_send_email)
})
.await?;
@@ -690,6 +889,7 @@ fn do_send_local_notifs(
comment: &Comment,
user: &User_,
post: &Post,
+ do_send_email: bool,
) -> Vec {
let mut recipient_ids = Vec::new();
let hostname = &format!("https://{}", Settings::get().hostname);
@@ -720,7 +920,7 @@ fn do_send_local_notifs(
};
// Send an email to those users that have notifications on
- if mention_user.send_notifications_to_email {
+ if do_send_email && mention_user.send_notifications_to_email {
if let Some(mention_email) = mention_user.email {
let subject = &format!("{} - Mentioned by {}", Settings::get().hostname, user.name,);
let html = &format!(
@@ -744,7 +944,7 @@ fn do_send_local_notifs(
if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) {
recipient_ids.push(parent_user.id);
- if parent_user.send_notifications_to_email {
+ if do_send_email && parent_user.send_notifications_to_email {
if let Some(comment_reply_email) = parent_user.email {
let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
let html = &format!(
@@ -767,7 +967,7 @@ fn do_send_local_notifs(
if let Ok(parent_user) = User_::read(&conn, post.creator_id) {
recipient_ids.push(parent_user.id);
- if parent_user.send_notifications_to_email {
+ if do_send_email && parent_user.send_notifications_to_email {
if let Some(post_reply_email) = parent_user.email {
let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
let html = &format!(
diff --git a/server/src/api/community.rs b/server/src/api/community.rs
index e5063e0ff..c5ae152aa 100644
--- a/server/src/api/community.rs
+++ b/server/src/api/community.rs
@@ -1,6 +1,6 @@
use super::*;
use crate::{
- api::{claims::Claims, APIError, Oper, Perform},
+ api::{claims::Claims, is_admin, is_mod_or_admin, APIError, Oper, Perform},
apub::ActorType,
blocking,
websocket::{
@@ -34,7 +34,6 @@ pub struct GetCommunity {
pub struct GetCommunityResponse {
pub community: CommunityView,
pub moderators: Vec,
- pub admins: Vec,
pub online: usize,
}
@@ -98,13 +97,24 @@ pub struct AddModToCommunityResponse {
#[derive(Serialize, Deserialize)]
pub struct EditCommunity {
pub edit_id: i32,
- name: String,
title: String,
description: Option,
category_id: i32,
- removed: Option,
- deleted: Option,
nsfw: bool,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeleteCommunity {
+ pub edit_id: i32,
+ deleted: bool,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RemoveCommunity {
+ pub edit_id: i32,
+ removed: bool,
reason: Option,
expires: Option,
auth: String,
@@ -185,13 +195,6 @@ impl Perform for Oper {
Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
};
- let site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
- let site_creator_id = site.creator_id;
- let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
- let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
- let creator_user = admins.remove(creator_index);
- admins.insert(0, creator_user);
-
let online = if let Some(ws) = websocket_info {
if let Some(id) = ws.id {
ws.chatserver.do_send(JoinCommunityRoom {
@@ -213,7 +216,6 @@ impl Perform for Oper {
let res = GetCommunityResponse {
community: community_view,
moderators,
- admins,
online,
};
@@ -264,6 +266,17 @@ impl Perform for Oper {
return Err(APIError::err("site_ban").into());
}
+ // Double check for duplicate community actor_ids
+ let actor_id = make_apub_endpoint(EndpointType::Community, &data.name).to_string();
+ let actor_id_cloned = actor_id.to_owned();
+ let community_dupe = blocking(pool, move |conn| {
+ Community::read_from_actor_id(conn, &actor_id_cloned)
+ })
+ .await?;
+ if community_dupe.is_ok() {
+ return Err(APIError::err("community_already_exists").into());
+ }
+
// When you create a community, make sure the user becomes a moderator and a follower
let keypair = generate_actor_keypair()?;
@@ -277,7 +290,7 @@ impl Perform for Oper {
deleted: None,
nsfw: data.nsfw,
updated: None,
- actor_id: make_apub_endpoint(EndpointType::Community, &data.name).to_string(),
+ actor_id,
local: true,
private_key: Some(keypair.private_key),
public_key: Some(keypair.public_key),
@@ -333,10 +346,6 @@ impl Perform for Oper {
) -> Result {
let data: &EditCommunity = &self.data;
- if let Err(slurs) = slur_check(&data.name) {
- return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
- }
-
if let Err(slurs) = slur_check(&data.title) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
@@ -352,10 +361,6 @@ impl Perform for Oper {
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
- if !is_valid_community_name(&data.name) {
- return Err(APIError::err("invalid_community_name").into());
- }
-
let user_id = claims.id;
// Check for a site ban
@@ -364,37 +369,28 @@ impl Perform for Oper {
return Err(APIError::err("site_ban").into());
}
- // Verify its a mod
+ // Verify its a mod (only mods can edit it)
let edit_id = data.edit_id;
- let mut editors: Vec = Vec::new();
- editors.append(
- &mut blocking(pool, move |conn| {
- CommunityModeratorView::for_community(conn, edit_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- editors.append(
- &mut blocking(pool, move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
- if !editors.contains(&user_id) {
- return Err(APIError::err("no_community_edit_allowed").into());
+ let mods: Vec = blocking(pool, move |conn| {
+ CommunityModeratorView::for_community(conn, edit_id)
+ .map(|v| v.into_iter().map(|m| m.user_id).collect())
+ })
+ .await??;
+ if !mods.contains(&user_id) {
+ return Err(APIError::err("not_a_moderator").into());
}
let edit_id = data.edit_id;
let read_community = blocking(pool, move |conn| Community::read(conn, edit_id)).await??;
let community_form = CommunityForm {
- name: data.name.to_owned(),
+ name: read_community.name,
title: data.title.to_owned(),
description: data.description.to_owned(),
category_id: data.category_id.to_owned(),
creator_id: read_community.creator_id,
- removed: data.removed.to_owned(),
- deleted: data.deleted.to_owned(),
+ removed: Some(read_community.removed),
+ deleted: Some(read_community.deleted),
nsfw: data.nsfw,
updated: Some(naive_now()),
actor_id: read_community.actor_id,
@@ -406,7 +402,7 @@ impl Perform for Oper {
};
let edit_id = data.edit_id;
- let updated_community = match blocking(pool, move |conn| {
+ match blocking(pool, move |conn| {
Community::update(conn, edit_id, &community_form)
})
.await?
@@ -415,42 +411,77 @@ impl Perform for Oper {
Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
};
- // Mod tables
- if let Some(removed) = data.removed.to_owned() {
- let expires = match data.expires {
- Some(time) => Some(naive_from_unix(time)),
- None => None,
- };
- let form = ModRemoveCommunityForm {
- mod_user_id: user_id,
- community_id: data.edit_id,
- removed: Some(removed),
- reason: data.reason.to_owned(),
- expires,
- };
- blocking(pool, move |conn| ModRemoveCommunity::create(conn, &form)).await??;
+ // TODO there needs to be some kind of an apub update
+ // process for communities and users
+
+ let edit_id = data.edit_id;
+ let community_view = blocking(pool, move |conn| {
+ CommunityView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ let res = CommunityResponse {
+ community: community_view,
+ };
+
+ send_community_websocket(&res, websocket_info, UserOperation::EditCommunity);
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = CommunityResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &DeleteCommunity = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
}
- if let Some(deleted) = data.deleted.to_owned() {
- if deleted {
- updated_community
- .send_delete(&user, &self.client, pool)
- .await?;
- } else {
- updated_community
- .send_undo_delete(&user, &self.client, pool)
- .await?;
- }
- } else if let Some(removed) = data.removed.to_owned() {
- if removed {
- updated_community
- .send_remove(&user, &self.client, pool)
- .await?;
- } else {
- updated_community
- .send_undo_remove(&user, &self.client, pool)
- .await?;
- }
+ // Verify its the creator (only a creator can delete the community)
+ let edit_id = data.edit_id;
+ let read_community = blocking(pool, move |conn| Community::read(conn, edit_id)).await??;
+ if read_community.creator_id != user_id {
+ return Err(APIError::err("no_community_edit_allowed").into());
+ }
+
+ // Do the delete
+ let edit_id = data.edit_id;
+ let deleted = data.deleted;
+ let updated_community = match blocking(pool, move |conn| {
+ Community::update_deleted(conn, edit_id, deleted)
+ })
+ .await?
+ {
+ Ok(community) => community,
+ Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
+ };
+
+ // Send apub messages
+ if deleted {
+ updated_community
+ .send_delete(&user, &self.client, pool)
+ .await?;
+ } else {
+ updated_community
+ .send_undo_delete(&user, &self.client, pool)
+ .await?;
}
let edit_id = data.edit_id;
@@ -463,20 +494,88 @@ impl Perform for Oper {
community: community_view,
};
- if let Some(ws) = websocket_info {
- // Strip out the user id and subscribed when sending to others
- let mut res_sent = res.clone();
- res_sent.community.user_id = None;
- res_sent.community.subscribed = None;
+ send_community_websocket(&res, websocket_info, UserOperation::DeleteCommunity);
- ws.chatserver.do_send(SendCommunityRoomMessage {
- op: UserOperation::EditCommunity,
- response: res_sent,
- community_id: data.edit_id,
- my_id: ws.id,
- });
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = CommunityResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &RemoveCommunity = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
}
+ // Verify its an admin (only an admin can remove a community)
+ is_admin(pool, user_id).await?;
+
+ // Do the remove
+ let edit_id = data.edit_id;
+ let removed = data.removed;
+ let updated_community = match blocking(pool, move |conn| {
+ Community::update_removed(conn, edit_id, removed)
+ })
+ .await?
+ {
+ Ok(community) => community,
+ Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
+ };
+
+ // Mod tables
+ let expires = match data.expires {
+ Some(time) => Some(naive_from_unix(time)),
+ None => None,
+ };
+ let form = ModRemoveCommunityForm {
+ mod_user_id: user_id,
+ community_id: data.edit_id,
+ removed: Some(removed),
+ reason: data.reason.to_owned(),
+ expires,
+ };
+ blocking(pool, move |conn| ModRemoveCommunity::create(conn, &form)).await??;
+
+ // Apub messages
+ if removed {
+ updated_community
+ .send_remove(&user, &self.client, pool)
+ .await?;
+ } else {
+ updated_community
+ .send_undo_remove(&user, &self.client, pool)
+ .await?;
+ }
+
+ let edit_id = data.edit_id;
+ let community_view = blocking(pool, move |conn| {
+ CommunityView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ let res = CommunityResponse {
+ community: community_view,
+ };
+
+ send_community_websocket(&res, websocket_info, UserOperation::RemoveCommunity);
+
Ok(res)
}
}
@@ -652,27 +751,10 @@ impl Perform for Oper {
let user_id = claims.id;
- let mut community_moderators: Vec = vec![];
-
let community_id = data.community_id;
- community_moderators.append(
- &mut blocking(pool, move |conn| {
- CommunityModeratorView::for_community(&conn, community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- community_moderators.append(
- &mut blocking(pool, move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
-
- if !community_moderators.contains(&user_id) {
- return Err(APIError::err("couldnt_update_community").into());
- }
+ // Verify that only mods or admins can ban
+ is_mod_or_admin(pool, user_id, community_id).await?;
let community_user_ban_form = CommunityUserBanForm {
community_id: data.community_id,
@@ -692,6 +774,7 @@ impl Perform for Oper {
}
// Mod tables
+ // TODO eventually do correct expires
let expires = match data.expires {
Some(time) => Some(naive_from_unix(time)),
None => None,
@@ -751,27 +834,10 @@ impl Perform for Oper {
user_id: data.user_id,
};
- let mut community_moderators: Vec = vec![];
-
let community_id = data.community_id;
- community_moderators.append(
- &mut blocking(pool, move |conn| {
- CommunityModeratorView::for_community(&conn, community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- community_moderators.append(
- &mut blocking(pool, move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
-
- if !community_moderators.contains(&user_id) {
- return Err(APIError::err("couldnt_update_community").into());
- }
+ // Verify that only mods or admins can add mod
+ is_mod_or_admin(pool, user_id, community_id).await?;
if data.added {
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@@ -850,26 +916,9 @@ impl Perform for Oper {
return Err(APIError::err("not_an_admin").into());
}
- let community_form = CommunityForm {
- name: read_community.name,
- title: read_community.title,
- description: read_community.description,
- category_id: read_community.category_id,
- creator_id: data.user_id, // This makes the new user the community creator
- removed: None,
- deleted: None,
- nsfw: read_community.nsfw,
- updated: Some(naive_now()),
- actor_id: read_community.actor_id,
- local: read_community.local,
- private_key: read_community.private_key,
- public_key: read_community.public_key,
- last_refreshed_at: None,
- published: None,
- };
-
let community_id = data.community_id;
- let update = move |conn: &'_ _| Community::update(conn, community_id, &community_form);
+ let new_creator = data.user_id;
+ let update = move |conn: &'_ _| Community::update_creator(conn, community_id, new_creator);
if blocking(pool, update).await?.is_err() {
return Err(APIError::err("couldnt_update_community").into());
};
@@ -939,8 +988,27 @@ impl Perform for Oper {
Ok(GetCommunityResponse {
community: community_view,
moderators,
- admins,
online: 0,
})
}
}
+
+pub fn send_community_websocket(
+ res: &CommunityResponse,
+ websocket_info: Option,
+ op: UserOperation,
+) {
+ if let Some(ws) = websocket_info {
+ // Strip out the user id and subscribed when sending to others
+ let mut res_sent = res.clone();
+ res_sent.community.user_id = None;
+ res_sent.community.subscribed = None;
+
+ ws.chatserver.do_send(SendCommunityRoomMessage {
+ op,
+ response: res_sent,
+ community_id: res.community.id,
+ my_id: ws.id,
+ });
+ }
+}
diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs
index bb65815ad..901172601 100644
--- a/server/src/api/mod.rs
+++ b/server/src/api/mod.rs
@@ -1,6 +1,14 @@
-use crate::{websocket::WebsocketInfo, DbPool, LemmyError};
+use crate::{blocking, websocket::WebsocketInfo, DbPool, LemmyError};
use actix_web::client::Client;
-use lemmy_db::{community::*, community_view::*, moderator::*, site::*, user::*, user_view::*};
+use lemmy_db::{
+ community::*,
+ community_view::*,
+ moderator::*,
+ site::*,
+ user::*,
+ user_view::*,
+ Crud,
+};
pub mod claims;
pub mod comment;
@@ -44,3 +52,25 @@ pub trait Perform {
websocket_info: Option,
) -> Result;
}
+
+pub async fn is_mod_or_admin(
+ pool: &DbPool,
+ user_id: i32,
+ community_id: i32,
+) -> Result<(), LemmyError> {
+ let is_mod_or_admin = blocking(pool, move |conn| {
+ Community::is_mod_or_admin(conn, user_id, community_id)
+ })
+ .await?;
+ if !is_mod_or_admin {
+ return Err(APIError::err("not_an_admin").into());
+ }
+ Ok(())
+}
+pub async fn is_admin(pool: &DbPool, user_id: i32) -> Result<(), LemmyError> {
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if !user.admin {
+ return Err(APIError::err("not_an_admin").into());
+ }
+ Ok(())
+}
diff --git a/server/src/api/post.rs b/server/src/api/post.rs
index b9518f0e9..79881c4b5 100644
--- a/server/src/api/post.rs
+++ b/server/src/api/post.rs
@@ -1,5 +1,5 @@
use crate::{
- api::{claims::Claims, APIError, Oper, Perform},
+ api::{claims::Claims, is_mod_or_admin, APIError, Oper, Perform},
apub::{ApubLikeableType, ApubObjectType},
blocking,
fetch_iframely_and_pictrs_data,
@@ -18,10 +18,8 @@ use lemmy_db::{
naive_now,
post::*,
post_view::*,
- site::*,
site_view::*,
user::*,
- user_view::*,
Crud,
Likeable,
ListingType,
@@ -37,6 +35,7 @@ use lemmy_utils::{
};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
+use url::Url;
#[derive(Serialize, Deserialize, Debug)]
pub struct CreatePost {
@@ -65,7 +64,6 @@ pub struct GetPostResponse {
comments: Vec,
community: CommunityView,
moderators: Vec,
- admins: Vec,
pub online: usize,
}
@@ -95,20 +93,42 @@ pub struct CreatePostLike {
#[derive(Serialize, Deserialize)]
pub struct EditPost {
pub edit_id: i32,
- creator_id: i32,
- community_id: i32,
name: String,
url: Option,
body: Option,
- removed: Option,
- deleted: Option,
nsfw: bool,
- locked: Option,
- stickied: Option,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeletePost {
+ pub edit_id: i32,
+ deleted: bool,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RemovePost {
+ pub edit_id: i32,
+ removed: bool,
reason: Option,
auth: String,
}
+#[derive(Serialize, Deserialize)]
+pub struct LockPost {
+ pub edit_id: i32,
+ locked: bool,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct StickyPost {
+ pub edit_id: i32,
+ stickied: bool,
+ auth: String,
+}
+
#[derive(Serialize, Deserialize)]
pub struct SavePost {
post_id: i32,
@@ -162,6 +182,13 @@ impl Perform for Oper {
return Err(APIError::err("site_ban").into());
}
+ if let Some(url) = data.url.as_ref() {
+ match Url::parse(url) {
+ Ok(_t) => (),
+ Err(_e) => return Err(APIError::err("invalid_url").into()),
+ }
+ }
+
// Fetch Iframely and pictrs cached image
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await;
@@ -303,14 +330,6 @@ impl Perform for Oper {
})
.await??;
- let site_creator_id =
- blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
-
- let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
- let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
- let creator_user = admins.remove(creator_index);
- admins.insert(0, creator_user);
-
let online = if let Some(ws) = websocket_info {
if let Some(id) = ws.id {
ws.chatserver.do_send(JoinPostRoom {
@@ -335,7 +354,6 @@ impl Perform for Oper {
comments,
community,
moderators,
- admins,
online,
})
}
@@ -541,35 +559,10 @@ impl Perform for Oper {
let user_id = claims.id;
let edit_id = data.edit_id;
- let read_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
-
- // Verify its the creator or a mod or admin
- let community_id = read_post.community_id;
- let mut editors: Vec = vec![read_post.creator_id];
- let mut moderators: Vec = vec![];
-
- moderators.append(
- &mut blocking(pool, move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- .map(|v| v.into_iter().map(|m| m.user_id).collect())
- })
- .await??,
- );
- moderators.append(
- &mut blocking(pool, move |conn| {
- UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())
- })
- .await??,
- );
-
- editors.extend(&moderators);
-
- if !editors.contains(&user_id) {
- return Err(APIError::err("no_post_edit_allowed").into());
- }
+ let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
// Check for a community ban
- let community_id = read_post.community_id;
+ let community_id = orig_post.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
@@ -582,55 +575,34 @@ impl Perform for Oper {
return Err(APIError::err("site_ban").into());
}
+ // Verify that only the creator can edit
+ if !Post::is_post_creator(user_id, orig_post.creator_id) {
+ return Err(APIError::err("no_post_edit_allowed").into());
+ }
+
// Fetch Iframely and Pictrs cached image
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await;
- let post_form = {
- // only modify some properties if they are a moderator
- if moderators.contains(&user_id) {
- PostForm {
- name: data.name.trim().to_owned(),
- url: data.url.to_owned(),
- body: data.body.to_owned(),
- creator_id: read_post.creator_id.to_owned(),
- community_id: read_post.community_id,
- removed: data.removed.to_owned(),
- deleted: data.deleted.to_owned(),
- nsfw: data.nsfw,
- locked: data.locked.to_owned(),
- stickied: data.stickied.to_owned(),
- updated: Some(naive_now()),
- embed_title: iframely_title,
- embed_description: iframely_description,
- embed_html: iframely_html,
- thumbnail_url: pictrs_thumbnail,
- ap_id: read_post.ap_id,
- local: read_post.local,
- published: None,
- }
- } else {
- PostForm {
- name: read_post.name.trim().to_owned(),
- url: data.url.to_owned(),
- body: data.body.to_owned(),
- creator_id: read_post.creator_id.to_owned(),
- community_id: read_post.community_id,
- removed: Some(read_post.removed),
- deleted: data.deleted.to_owned(),
- nsfw: data.nsfw,
- locked: Some(read_post.locked),
- stickied: Some(read_post.stickied),
- updated: Some(naive_now()),
- embed_title: iframely_title,
- embed_description: iframely_description,
- embed_html: iframely_html,
- thumbnail_url: pictrs_thumbnail,
- ap_id: read_post.ap_id,
- local: read_post.local,
- published: None,
- }
- }
+ let post_form = PostForm {
+ name: data.name.trim().to_owned(),
+ url: data.url.to_owned(),
+ body: data.body.to_owned(),
+ nsfw: data.nsfw,
+ creator_id: orig_post.creator_id.to_owned(),
+ community_id: orig_post.community_id,
+ removed: Some(orig_post.removed),
+ deleted: Some(orig_post.deleted),
+ locked: Some(orig_post.locked),
+ stickied: Some(orig_post.stickied),
+ updated: Some(naive_now()),
+ embed_title: iframely_title,
+ embed_description: iframely_description,
+ embed_html: iframely_html,
+ thumbnail_url: pictrs_thumbnail,
+ ap_id: orig_post.ap_id,
+ local: orig_post.local,
+ published: None,
};
let edit_id = data.edit_id;
@@ -648,58 +620,8 @@ impl Perform for Oper {
}
};
- if moderators.contains(&user_id) {
- // Mod tables
- if let Some(removed) = data.removed.to_owned() {
- let form = ModRemovePostForm {
- mod_user_id: user_id,
- post_id: data.edit_id,
- removed: Some(removed),
- reason: data.reason.to_owned(),
- };
- blocking(pool, move |conn| ModRemovePost::create(conn, &form)).await??;
- }
-
- if let Some(locked) = data.locked.to_owned() {
- let form = ModLockPostForm {
- mod_user_id: user_id,
- post_id: data.edit_id,
- locked: Some(locked),
- };
- blocking(pool, move |conn| ModLockPost::create(conn, &form)).await??;
- }
-
- if let Some(stickied) = data.stickied.to_owned() {
- let form = ModStickyPostForm {
- mod_user_id: user_id,
- post_id: data.edit_id,
- stickied: Some(stickied),
- };
- blocking(pool, move |conn| ModStickyPost::create(conn, &form)).await??;
- }
- }
-
- if let Some(deleted) = data.deleted.to_owned() {
- if deleted {
- updated_post.send_delete(&user, &self.client, pool).await?;
- } else {
- updated_post
- .send_undo_delete(&user, &self.client, pool)
- .await?;
- }
- } else if let Some(removed) = data.removed.to_owned() {
- if moderators.contains(&user_id) {
- if removed {
- updated_post.send_remove(&user, &self.client, pool).await?;
- } else {
- updated_post
- .send_undo_remove(&user, &self.client, pool)
- .await?;
- }
- }
- } else {
- updated_post.send_update(&user, &self.client, pool).await?;
- }
+ // Send apub update
+ updated_post.send_update(&user, &self.client, pool).await?;
let edit_id = data.edit_id;
let post_view = blocking(pool, move |conn| {
@@ -721,6 +643,324 @@ impl Perform for Oper {
}
}
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = PostResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &DeletePost = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let edit_id = data.edit_id;
+ let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ // Check for a community ban
+ let community_id = orig_post.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only the creator can delete
+ if !Post::is_post_creator(user_id, orig_post.creator_id) {
+ return Err(APIError::err("no_post_edit_allowed").into());
+ }
+
+ // Update the post
+ let edit_id = data.edit_id;
+ let deleted = data.deleted;
+ let updated_post = blocking(pool, move |conn| {
+ Post::update_deleted(conn, edit_id, deleted)
+ })
+ .await??;
+
+ // apub updates
+ if deleted {
+ updated_post.send_delete(&user, &self.client, pool).await?;
+ } else {
+ updated_post
+ .send_undo_delete(&user, &self.client, pool)
+ .await?;
+ }
+
+ // Refetch the post
+ let edit_id = data.edit_id;
+ let post_view = blocking(pool, move |conn| {
+ PostView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ let res = PostResponse { post: post_view };
+
+ if let Some(ws) = websocket_info {
+ ws.chatserver.do_send(SendPost {
+ op: UserOperation::DeletePost,
+ post: res.clone(),
+ my_id: ws.id,
+ });
+ }
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = PostResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &RemovePost = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let edit_id = data.edit_id;
+ let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ // Check for a community ban
+ let community_id = orig_post.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only the mods can remove
+ is_mod_or_admin(pool, user_id, community_id).await?;
+
+ // Update the post
+ let edit_id = data.edit_id;
+ let removed = data.removed;
+ let updated_post = blocking(pool, move |conn| {
+ Post::update_removed(conn, edit_id, removed)
+ })
+ .await??;
+
+ // Mod tables
+ let form = ModRemovePostForm {
+ mod_user_id: user_id,
+ post_id: data.edit_id,
+ removed: Some(removed),
+ reason: data.reason.to_owned(),
+ };
+ blocking(pool, move |conn| ModRemovePost::create(conn, &form)).await??;
+
+ // apub updates
+ if removed {
+ updated_post.send_remove(&user, &self.client, pool).await?;
+ } else {
+ updated_post
+ .send_undo_remove(&user, &self.client, pool)
+ .await?;
+ }
+
+ // Refetch the post
+ let edit_id = data.edit_id;
+ let post_view = blocking(pool, move |conn| {
+ PostView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ let res = PostResponse { post: post_view };
+
+ if let Some(ws) = websocket_info {
+ ws.chatserver.do_send(SendPost {
+ op: UserOperation::RemovePost,
+ post: res.clone(),
+ my_id: ws.id,
+ });
+ }
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = PostResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &LockPost = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let edit_id = data.edit_id;
+ let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ // Check for a community ban
+ let community_id = orig_post.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only the mods can lock
+ is_mod_or_admin(pool, user_id, community_id).await?;
+
+ // Update the post
+ let edit_id = data.edit_id;
+ let locked = data.locked;
+ let updated_post =
+ blocking(pool, move |conn| Post::update_locked(conn, edit_id, locked)).await??;
+
+ // Mod tables
+ let form = ModLockPostForm {
+ mod_user_id: user_id,
+ post_id: data.edit_id,
+ locked: Some(locked),
+ };
+ blocking(pool, move |conn| ModLockPost::create(conn, &form)).await??;
+
+ // apub updates
+ updated_post.send_update(&user, &self.client, pool).await?;
+
+ // Refetch the post
+ let edit_id = data.edit_id;
+ let post_view = blocking(pool, move |conn| {
+ PostView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ let res = PostResponse { post: post_view };
+
+ if let Some(ws) = websocket_info {
+ ws.chatserver.do_send(SendPost {
+ op: UserOperation::LockPost,
+ post: res.clone(),
+ my_id: ws.id,
+ });
+ }
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = PostResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result {
+ let data: &StickyPost = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err("not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let edit_id = data.edit_id;
+ let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
+
+ // Check for a site ban
+ let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
+ if user.banned {
+ return Err(APIError::err("site_ban").into());
+ }
+
+ // Check for a community ban
+ let community_id = orig_post.community_id;
+ let is_banned =
+ move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
+ if blocking(pool, is_banned).await? {
+ return Err(APIError::err("community_ban").into());
+ }
+
+ // Verify that only the mods can sticky
+ is_mod_or_admin(pool, user_id, community_id).await?;
+
+ // Update the post
+ let edit_id = data.edit_id;
+ let stickied = data.stickied;
+ let updated_post = blocking(pool, move |conn| {
+ Post::update_stickied(conn, edit_id, stickied)
+ })
+ .await??;
+
+ // Mod tables
+ let form = ModStickyPostForm {
+ mod_user_id: user_id,
+ post_id: data.edit_id,
+ stickied: Some(stickied),
+ };
+ blocking(pool, move |conn| ModStickyPost::create(conn, &form)).await??;
+
+ // Apub updates
+ // TODO stickied should pry work like locked for ease of use
+ updated_post.send_update(&user, &self.client, pool).await?;
+
+ // Refetch the post
+ let edit_id = data.edit_id;
+ let post_view = blocking(pool, move |conn| {
+ PostView::read(conn, edit_id, Some(user_id))
+ })
+ .await??;
+
+ let res = PostResponse { post: post_view };
+
+ if let Some(ws) = websocket_info {
+ ws.chatserver.do_send(SendPost {
+ op: UserOperation::StickyPost,
+ post: res.clone(),
+ my_id: ws.id,
+ });
+ }
+
+ Ok(res)
+ }
+}
+
#[async_trait::async_trait(?Send)]
impl Perform for Oper {
type Response = PostResponse;
diff --git a/server/src/api/site.rs b/server/src/api/site.rs
index 241a80e31..85511e6c9 100644
--- a/server/src/api/site.rs
+++ b/server/src/api/site.rs
@@ -1,8 +1,9 @@
use super::user::Register;
use crate::{
- api::{claims::Claims, APIError, Oper, Perform},
+ api::{claims::Claims, is_admin, APIError, Oper, Perform},
apub::fetcher::search_by_apub_id,
blocking,
+ version,
websocket::{server::SendAllMessage, UserOperation, WebsocketInfo},
DbPool,
LemmyError,
@@ -110,6 +111,7 @@ pub struct GetSiteResponse {
admins: Vec,
banned: Vec,
pub online: usize,
+ version: String,
}
#[derive(Serialize, Deserialize)]
@@ -255,10 +257,7 @@ impl Perform for Oper {
let user_id = claims.id;
// Make sure user is an admin
- let user = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
- if !user.admin {
- return Err(APIError::err("not_an_admin").into());
- }
+ is_admin(pool, user_id).await?;
let site_form = SiteForm {
name: data.name.to_owned(),
@@ -309,10 +308,7 @@ impl Perform for Oper {
let user_id = claims.id;
// Make sure user is an admin
- let user = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
- if !user.admin {
- return Err(APIError::err("not_an_admin").into());
- }
+ is_admin(pool, user_id).await?;
let found_site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
@@ -424,6 +420,7 @@ impl Perform for Oper {
admins,
banned,
online,
+ version: version::VERSION.to_string(),
})
}
}
@@ -666,6 +663,7 @@ impl Perform for Oper {
admins,
banned,
online: 0,
+ version: version::VERSION.to_string(),
})
}
}
@@ -689,12 +687,7 @@ impl Perform for Oper {
let user_id = claims.id;
// Only let admins read this
- let admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
- let admin_ids: Vec = admins.into_iter().map(|m| m.id).collect();
-
- if !admin_ids.contains(&user_id) {
- return Err(APIError::err("not_an_admin").into());
- }
+ is_admin(pool, user_id).await?;
let config_hjson = Settings::read_config_file()?;
diff --git a/server/src/api/user.rs b/server/src/api/user.rs
index ddcf2ef2e..32a16b00e 100644
--- a/server/src/api/user.rs
+++ b/server/src/api/user.rs
@@ -1,5 +1,5 @@
use crate::{
- api::{claims::Claims, APIError, Oper, Perform},
+ api::{claims::Claims, is_admin, APIError, Oper, Perform},
apub::ApubObjectType,
blocking,
websocket::{
@@ -110,7 +110,6 @@ pub struct GetUserDetailsResponse {
moderates: Vec,
comments: Vec,
posts: Vec,
- admins: Vec,
}
#[derive(Serialize, Deserialize)]
@@ -174,9 +173,9 @@ pub struct GetUserMentions {
}
#[derive(Serialize, Deserialize)]
-pub struct EditUserMention {
+pub struct MarkUserMentionAsRead {
user_mention_id: i32,
- read: Option,
+ read: bool,
auth: String,
}
@@ -216,9 +215,21 @@ pub struct CreatePrivateMessage {
#[derive(Serialize, Deserialize)]
pub struct EditPrivateMessage {
edit_id: i32,
- content: Option,
- deleted: Option,
- read: Option,
+ content: String,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeletePrivateMessage {
+ edit_id: i32,
+ deleted: bool,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct MarkPrivateMessageAsRead {
+ edit_id: i32,
+ read: bool,
auth: String,
}
@@ -264,7 +275,7 @@ impl Perform for Oper {
// Fetch that username / email
let username_or_email = data.username_or_email.clone();
let user = match blocking(pool, move |conn| {
- Claims::find_by_email_or_username(conn, &username_or_email)
+ User_::find_by_email_or_username(conn, &username_or_email)
})
.await?
{
@@ -631,20 +642,10 @@ impl Perform for Oper {
})
.await??;
- let site_creator_id =
- blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
-
- let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
- let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
- let creator_user = admins.remove(creator_index);
- admins.insert(0, creator_user);
-
- // If its not the same user, remove the email
- if let Some(user_id) = user_id {
- if user_details_id != user_id {
- user_view.email = None;
- }
- } else {
+ // If its not the same user, remove the email, and settings
+ // TODO an if let chain would be better here, but can't figure it out
+ // TODO separate out settings into its own thing
+ if user_id.is_none() || user_details_id != user_id.unwrap_or(0) {
user_view.email = None;
}
@@ -655,7 +656,6 @@ impl Perform for Oper {
moderates,
comments,
posts,
- admins,
})
}
}
@@ -679,10 +679,7 @@ impl Perform for Oper {
let user_id = claims.id;
// Make sure user is an admin
- let is_admin = move |conn: &'_ _| UserView::read(conn, user_id).map(|u| u.admin);
- if !blocking(pool, is_admin).await?? {
- return Err(APIError::err("not_an_admin").into());
- }
+ is_admin(pool, user_id).await?;
let added = data.added;
let added_user_id = data.user_id;
@@ -741,10 +738,7 @@ impl Perform for Oper {
let user_id = claims.id;
// Make sure user is an admin
- let is_admin = move |conn: &'_ _| UserView::read(conn, user_id).map(|u| u.admin);
- if !blocking(pool, is_admin).await?? {
- return Err(APIError::err("not_an_admin").into());
- }
+ is_admin(pool, user_id).await?;
let ban = data.ban;
let banned_user_id = data.user_id;
@@ -864,7 +858,7 @@ impl Perform for Oper {
}
#[async_trait::async_trait(?Send)]
-impl Perform for Oper {
+impl Perform for Oper {
type Response = UserMentionResponse;
async fn perform(
@@ -872,7 +866,7 @@ impl Perform for Oper {
pool: &DbPool,
_websocket_info: Option,
) -> Result {
- let data: &EditUserMention = &self.data;
+ let data: &MarkUserMentionAsRead = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
@@ -880,28 +874,23 @@ impl Perform for Oper {
};
let user_id = claims.id;
- if user_id != data.user_mention_id {
+
+ let user_mention_id = data.user_mention_id;
+ let read_user_mention =
+ blocking(pool, move |conn| UserMention::read(conn, user_mention_id)).await??;
+
+ if user_id != read_user_mention.recipient_id {
return Err(APIError::err("couldnt_update_comment").into());
}
- let user_mention_id = data.user_mention_id;
- let user_mention =
- blocking(pool, move |conn| UserMention::read(conn, user_mention_id)).await??;
-
- let user_mention_form = UserMentionForm {
- recipient_id: user_id,
- comment_id: user_mention.comment_id,
- read: data.read.to_owned(),
- };
-
- let user_mention_id = user_mention.id;
- let update_mention =
- move |conn: &'_ _| UserMention::update(conn, user_mention_id, &user_mention_form);
+ let user_mention_id = read_user_mention.id;
+ let read = data.read;
+ let update_mention = move |conn: &'_ _| UserMention::update_read(conn, user_mention_id, read);
if blocking(pool, update_mention).await?.is_err() {
return Err(APIError::err("couldnt_update_comment").into());
};
- let user_mention_id = user_mention.id;
+ let user_mention_id = read_user_mention.id;
let user_mention_view = blocking(pool, move |conn| {
UserMentionView::read(conn, user_mention_id, user_id)
})
@@ -941,70 +930,26 @@ impl Perform for Oper {
.await??;
// TODO: this should probably be a bulk operation
+ // Not easy to do as a bulk operation,
+ // because recipient_id isn't in the comment table
for reply in &replies {
let reply_id = reply.id;
- let mark_as_read = move |conn: &'_ _| Comment::mark_as_read(conn, reply_id);
+ let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
if blocking(pool, mark_as_read).await?.is_err() {
return Err(APIError::err("couldnt_update_comment").into());
}
}
- // Mentions
- let mentions = blocking(pool, move |conn| {
- UserMentionQueryBuilder::create(conn, user_id)
- .unread_only(true)
- .page(1)
- .limit(999)
- .list()
- })
- .await??;
-
- // TODO: this should probably be a bulk operation
- for mention in &mentions {
- let mention_form = UserMentionForm {
- recipient_id: mention.to_owned().recipient_id,
- comment_id: mention.to_owned().id,
- read: Some(true),
- };
-
- let user_mention_id = mention.user_mention_id;
- let update_mention =
- move |conn: &'_ _| UserMention::update(conn, user_mention_id, &mention_form);
- if blocking(pool, update_mention).await?.is_err() {
- return Err(APIError::err("couldnt_update_comment").into());
- }
+ // Mark all user mentions as read
+ let update_user_mentions = move |conn: &'_ _| UserMention::mark_all_as_read(conn, user_id);
+ if blocking(pool, update_user_mentions).await?.is_err() {
+ return Err(APIError::err("couldnt_update_comment").into());
}
- // messages
- let messages = blocking(pool, move |conn| {
- PrivateMessageQueryBuilder::create(conn, user_id)
- .page(1)
- .limit(999)
- .unread_only(true)
- .list()
- })
- .await??;
-
- // TODO: this should probably be a bulk operation
- for message in &messages {
- let private_message_form = PrivateMessageForm {
- content: message.to_owned().content,
- creator_id: message.to_owned().creator_id,
- recipient_id: message.to_owned().recipient_id,
- deleted: None,
- read: Some(true),
- updated: None,
- ap_id: message.to_owned().ap_id,
- local: message.local,
- published: None,
- };
-
- let message_id = message.id;
- let update_pm =
- move |conn: &'_ _| PrivateMessage::update(conn, message_id, &private_message_form);
- if blocking(pool, update_pm).await?.is_err() {
- return Err(APIError::err("couldnt_update_private_message").into());
- }
+ // Mark all private_messages as read
+ let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, user_id);
+ if blocking(pool, update_pm).await?.is_err() {
+ return Err(APIError::err("couldnt_update_private_message").into());
}
Ok(GetRepliesResponse { replies: vec![] })
@@ -1294,59 +1239,25 @@ impl Perform for Oper {
let user_id = claims.id;
- let edit_id = data.edit_id;
- let orig_private_message =
- blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
-
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
- // Check to make sure they are the creator (or the recipient marking as read
- if !(data.read.is_some() && orig_private_message.recipient_id.eq(&user_id)
- || orig_private_message.creator_id.eq(&user_id))
- {
+ // Checking permissions
+ let edit_id = data.edit_id;
+ let orig_private_message =
+ blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
+ if user_id != orig_private_message.creator_id {
return Err(APIError::err("no_private_message_edit_allowed").into());
}
- let content_slurs_removed = match &data.content {
- Some(content) => remove_slurs(content),
- None => orig_private_message.content.clone(),
- };
-
- let private_message_form = {
- if data.read.is_some() {
- PrivateMessageForm {
- content: orig_private_message.content.to_owned(),
- creator_id: orig_private_message.creator_id,
- recipient_id: orig_private_message.recipient_id,
- read: data.read.to_owned(),
- updated: orig_private_message.updated,
- deleted: Some(orig_private_message.deleted),
- ap_id: orig_private_message.ap_id,
- local: orig_private_message.local,
- published: None,
- }
- } else {
- PrivateMessageForm {
- content: content_slurs_removed,
- creator_id: orig_private_message.creator_id,
- recipient_id: orig_private_message.recipient_id,
- deleted: data.deleted.to_owned(),
- read: Some(orig_private_message.read),
- updated: Some(naive_now()),
- ap_id: orig_private_message.ap_id,
- local: orig_private_message.local,
- published: None,
- }
- }
- };
-
+ // Doing the update
+ let content_slurs_removed = remove_slurs(&data.content);
let edit_id = data.edit_id;
let updated_private_message = match blocking(pool, move |conn| {
- PrivateMessage::update(conn, edit_id, &private_message_form)
+ PrivateMessage::update_content(conn, edit_id, &content_slurs_removed)
})
.await?
{
@@ -1354,30 +1265,14 @@ impl Perform for Oper {
Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
};
- if data.read.is_none() {
- if let Some(deleted) = data.deleted.to_owned() {
- if deleted {
- updated_private_message
- .send_delete(&user, &self.client, pool)
- .await?;
- } else {
- updated_private_message
- .send_undo_delete(&user, &self.client, pool)
- .await?;
- }
- } else {
- updated_private_message
- .send_update(&user, &self.client, pool)
- .await?;
- }
- } else {
- updated_private_message
- .send_update(&user, &self.client, pool)
- .await?;
- }
+ // Send the apub update
+ updated_private_message
+ .send_update(&user, &self.client, pool)
+ .await?;
let edit_id = data.edit_id;
let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
+ let recipient_id = message.recipient_id;
let res = PrivateMessageResponse { message };
@@ -1385,7 +1280,146 @@ impl Perform for Oper {
ws.chatserver.do_send(SendUserRoomMessage {
op: UserOperation::EditPrivateMessage,
response: res.clone(),
- recipient_id: orig_private_message.recipient_id,
+ recipient_id,
+ my_id: ws.id,
+ });
+ }
+
+ Ok(res)
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Oper {
+ type Response = PrivateMessageResponse;
+
+ async fn perform(
+ &self,
+ pool: &DbPool,
+ websocket_info: Option,
+ ) -> Result