Done merging http-api and private_message

This commit is contained in:
Dessalines 2020-01-23 19:17:42 -05:00
commit ac1d5f2b86
32 changed files with 1328 additions and 1052 deletions

2
docs/src/SUMMARY.md vendored
View file

@ -12,5 +12,5 @@
- [Contributing](contributing.md) - [Contributing](contributing.md)
- [Docker Development](contributing_docker_development.md) - [Docker Development](contributing_docker_development.md)
- [Local Development](contributing_local_development.md) - [Local Development](contributing_local_development.md)
- [Websocket API](contributing_websocket_api.md) - [Websocket/HTTP API](contributing_websocket_http_api.md)
- [ActivityPub API Outline](contributing_apub_api_outline.md) - [ActivityPub API Outline](contributing_apub_api_outline.md)

View file

@ -5,126 +5,171 @@
- [Data types](#data-types) - [Data types](#data-types)
- [Basic usage](#basic-usage) - [Basic usage](#basic-usage)
* [WebSocket Endpoint](#websocket-endpoint) * [WebSocket](#websocket)
* [Testing with Websocat](#testing-with-websocat) + [Testing with Websocat](#testing-with-websocat)
* [Testing with the WebSocket JavaScript API](#testing-with-the-websocket-javascript-api) + [Testing with the WebSocket JavaScript API](#testing-with-the-websocket-javascript-api)
* [HTTP](#http)
+ [Testing with Curl](#testing-with-curl)
- [Get Example](#get-example)
- [Post Example](#post-example)
- [Rate limits](#rate-limits) - [Rate limits](#rate-limits)
- [Errors](#errors) - [Errors](#errors)
- [API documentation](#api-documentation) - [API documentation](#api-documentation)
* [Sort Types](#sort-types) * [Sort Types](#sort-types)
* [Websocket vs HTTP](#websocket-vs-http)
* [User / Authentication / Admin actions](#user--authentication--admin-actions) * [User / Authentication / Admin actions](#user--authentication--admin-actions)
+ [Login](#login) + [Login](#login)
- [Request](#request) - [Request](#request)
- [Response](#response) - [Response](#response)
- [HTTP](#http-1)
+ [Register](#register) + [Register](#register)
- [Request](#request-1) - [Request](#request-1)
- [Response](#response-1) - [Response](#response-1)
- [HTTP](#http-2)
+ [Get User Details](#get-user-details) + [Get User Details](#get-user-details)
- [Request](#request-2) - [Request](#request-2)
- [Response](#response-2) - [Response](#response-2)
- [HTTP](#http-3)
+ [Save User Settings](#save-user-settings) + [Save User Settings](#save-user-settings)
- [Request](#request-3) - [Request](#request-3)
- [Response](#response-3) - [Response](#response-3)
- [HTTP](#http-4)
+ [Get Replies / Inbox](#get-replies--inbox) + [Get Replies / Inbox](#get-replies--inbox)
- [Request](#request-4) - [Request](#request-4)
- [Response](#response-4) - [Response](#response-4)
- [HTTP](#http-5)
+ [Get User Mentions](#get-user-mentions) + [Get User Mentions](#get-user-mentions)
- [Request](#request-5) - [Request](#request-5)
- [Response](#response-5) - [Response](#response-5)
+ [Mark All As Read](#mark-all-as-read) - [HTTP](#http-6)
+ [Edit User Mention](#edit-user-mention)
- [Request](#request-6) - [Request](#request-6)
- [Response](#response-6) - [Response](#response-6)
+ [Delete Account](#delete-account) - [HTTP](#http-7)
+ [Mark All As Read](#mark-all-as-read)
- [Request](#request-7) - [Request](#request-7)
- [Response](#response-7) - [Response](#response-7)
+ [Add admin](#add-admin) - [HTTP](#http-8)
+ [Delete Account](#delete-account)
- [Request](#request-8) - [Request](#request-8)
- [Response](#response-8) - [Response](#response-8)
+ [Ban user](#ban-user) - [HTTP](#http-9)
+ [Add admin](#add-admin)
- [Request](#request-9) - [Request](#request-9)
- [Response](#response-9) - [Response](#response-9)
* [Site](#site) - [HTTP](#http-10)
+ [List Categories](#list-categories) + [Ban user](#ban-user)
- [Request](#request-10) - [Request](#request-10)
- [Response](#response-10) - [Response](#response-10)
+ [Search](#search) - [HTTP](#http-11)
* [Site](#site)
+ [List Categories](#list-categories)
- [Request](#request-11) - [Request](#request-11)
- [Response](#response-11) - [Response](#response-11)
+ [Get Modlog](#get-modlog) - [HTTP](#http-12)
+ [Search](#search)
- [Request](#request-12) - [Request](#request-12)
- [Response](#response-12) - [Response](#response-12)
+ [Create Site](#create-site) - [HTTP](#http-13)
+ [Get Modlog](#get-modlog)
- [Request](#request-13) - [Request](#request-13)
- [Response](#response-13) - [Response](#response-13)
+ [Edit Site](#edit-site) - [HTTP](#http-14)
+ [Create Site](#create-site)
- [Request](#request-14) - [Request](#request-14)
- [Response](#response-14) - [Response](#response-14)
+ [Get Site](#get-site) - [HTTP](#http-15)
+ [Edit Site](#edit-site)
- [Request](#request-15) - [Request](#request-15)
- [Response](#response-15) - [Response](#response-15)
+ [Transfer Site](#transfer-site) - [HTTP](#http-16)
+ [Get Site](#get-site)
- [Request](#request-16) - [Request](#request-16)
- [Response](#response-16) - [Response](#response-16)
* [Community](#community) - [HTTP](#http-17)
+ [Get Community](#get-community) + [Transfer Site](#transfer-site)
- [Request](#request-17) - [Request](#request-17)
- [Response](#response-17) - [Response](#response-17)
+ [Create Community](#create-community) - [HTTP](#http-18)
* [Community](#community)
+ [Get Community](#get-community)
- [Request](#request-18) - [Request](#request-18)
- [Response](#response-18) - [Response](#response-18)
+ [List Communities](#list-communities) - [HTTP](#http-19)
+ [Create Community](#create-community)
- [Request](#request-19) - [Request](#request-19)
- [Response](#response-19) - [Response](#response-19)
+ [Ban from Community](#ban-from-community) - [HTTP](#http-20)
+ [List Communities](#list-communities)
- [Request](#request-20) - [Request](#request-20)
- [Response](#response-20) - [Response](#response-20)
+ [Add Mod to Community](#add-mod-to-community) - [HTTP](#http-21)
+ [Ban from Community](#ban-from-community)
- [Request](#request-21) - [Request](#request-21)
- [Response](#response-21) - [Response](#response-21)
+ [Edit Community](#edit-community) - [HTTP](#http-22)
+ [Add Mod to Community](#add-mod-to-community)
- [Request](#request-22) - [Request](#request-22)
- [Response](#response-22) - [Response](#response-22)
+ [Follow Community](#follow-community) - [HTTP](#http-23)
+ [Edit Community](#edit-community)
- [Request](#request-23) - [Request](#request-23)
- [Response](#response-23) - [Response](#response-23)
+ [Get Followed Communities](#get-followed-communities) - [HTTP](#http-24)
+ [Follow Community](#follow-community)
- [Request](#request-24) - [Request](#request-24)
- [Response](#response-24) - [Response](#response-24)
+ [Transfer Community](#transfer-community) - [HTTP](#http-25)
+ [Get Followed Communities](#get-followed-communities)
- [Request](#request-25) - [Request](#request-25)
- [Response](#response-25) - [Response](#response-25)
* [Post](#post) - [HTTP](#http-26)
+ [Create Post](#create-post) + [Transfer Community](#transfer-community)
- [Request](#request-26) - [Request](#request-26)
- [Response](#response-26) - [Response](#response-26)
+ [Get Post](#get-post) - [HTTP](#http-27)
* [Post](#post)
+ [Create Post](#create-post)
- [Request](#request-27) - [Request](#request-27)
- [Response](#response-27) - [Response](#response-27)
+ [Get Posts](#get-posts) - [HTTP](#http-28)
+ [Get Post](#get-post)
- [Request](#request-28) - [Request](#request-28)
- [Response](#response-28) - [Response](#response-28)
+ [Create Post Like](#create-post-like) - [HTTP](#http-29)
+ [Get Posts](#get-posts)
- [Request](#request-29) - [Request](#request-29)
- [Response](#response-29) - [Response](#response-29)
+ [Edit Post](#edit-post) - [HTTP](#http-30)
+ [Create Post Like](#create-post-like)
- [Request](#request-30) - [Request](#request-30)
- [Response](#response-30) - [Response](#response-30)
+ [Save Post](#save-post) - [HTTP](#http-31)
+ [Edit Post](#edit-post)
- [Request](#request-31) - [Request](#request-31)
- [Response](#response-31) - [Response](#response-31)
* [Comment](#comment) - [HTTP](#http-32)
+ [Create Comment](#create-comment) + [Save Post](#save-post)
- [Request](#request-32) - [Request](#request-32)
- [Response](#response-32) - [Response](#response-32)
+ [Edit Comment](#edit-comment) - [HTTP](#http-33)
* [Comment](#comment)
+ [Create Comment](#create-comment)
- [Request](#request-33) - [Request](#request-33)
- [Response](#response-33) - [Response](#response-33)
+ [Save Comment](#save-comment) - [HTTP](#http-34)
+ [Edit Comment](#edit-comment)
- [Request](#request-34) - [Request](#request-34)
- [Response](#response-34) - [Response](#response-34)
+ [Create Comment Like](#create-comment-like) - [HTTP](#http-35)
+ [Save Comment](#save-comment)
- [Request](#request-35) - [Request](#request-35)
- [Response](#response-35) - [Response](#response-35)
- [HTTP](#http-36)
+ [Create Comment Like](#create-comment-like)
- [Request](#request-36)
- [Response](#response-36)
- [HTTP](#http-37)
* [RSS / Atom feeds](#rss--atom-feeds) * [RSS / Atom feeds](#rss--atom-feeds)
+ [All](#all) + [All](#all)
+ [Community](#community-1) + [Community](#community-1)
@ -144,13 +189,13 @@
Request and response strings are in [JSON format](https://www.json.org). Request and response strings are in [JSON format](https://www.json.org).
### WebSocket Endpoint ### WebSocket
Connect to <code>ws://***host***/api/v1/ws</code> to get started. Connect to <code>ws://***host***/api/v1/ws</code> to get started.
If the ***`host`*** supports secure connections, you can use <code>wss://***host***/api/v1/ws</code>. If the ***`host`*** supports secure connections, you can use <code>wss://***host***/api/v1/ws</code>.
### Testing with Websocat #### Testing with Websocat
[Websocat link](https://github.com/vi/websocat) [Websocat link](https://github.com/vi/websocat)
@ -159,7 +204,7 @@ If the ***`host`*** supports secure connections, you can use <code>wss://***host
A simple test command: A simple test command:
`{"op": "ListCategories"}` `{"op": "ListCategories"}`
### Testing with the WebSocket JavaScript API #### Testing with the WebSocket JavaScript API
[WebSocket JavaScript API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) [WebSocket JavaScript API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
```javascript ```javascript
@ -171,6 +216,32 @@ ws.onopen = function () {
})); }));
}; };
``` ```
### HTTP
Endpoints are at <code>http://***host***/api/v1/***endpoint***</code>. They'll be listed below for each action.
#### Testing with Curl
##### Get Example
```
curl /community/list?sort=Hot
```
##### Post Example
```
curl -i -H \
"Content-Type: application/json" \
-X POST \
-d '{
"comment_id": X,
"post_id": X,
"score": X,
"auth": "..."
}' \
/comment/like
```
## Rate limits ## Rate limits
@ -201,6 +272,11 @@ These go wherever there is a `sort` field. The available sort types are:
- `TopYear` - the most upvoted posts/communities of the current year. - `TopYear` - the most upvoted posts/communities of the current year.
- `TopAll` - the most upvoted posts/communities on the current instance. - `TopAll` - the most upvoted posts/communities on the current instance.
### Websocket vs HTTP
- Below are the websocket JSON requests / responses. For HTTP, ignore all fields except those inside `data`.
- For example, an http login will be a `POST` `{username_or_email: X, password: X}`
### User / Authentication / Admin actions ### User / Authentication / Admin actions
#### Login #### Login
@ -220,13 +296,19 @@ The `jwt` string should be stored and used anywhere `auth` is called for.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "Login",
jwt: String data: {
jwt: String,
}
} }
``` ```
##### HTTP
`POST /user/login`
#### Register #### Register
Only the first user will be able to be the admin. Only the first user will be able to be the admin.
##### Request ##### Request
@ -245,11 +327,17 @@ Only the first user will be able to be the admin.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "Register",
jwt: String data: {
jwt: String,
}
} }
``` ```
##### HTTP
`POST /user/register`
#### Get User Details #### Get User Details
##### Request ##### Request
```rust ```rust
@ -270,14 +358,20 @@ Only the first user will be able to be the admin.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetUserDetails",
data: {
user: UserView, user: UserView,
follows: Vec<CommunityFollowerView>, follows: Vec<CommunityFollowerView>,
moderates: Vec<CommunityModeratorView>, moderates: Vec<CommunityModeratorView>,
comments: Vec<CommentView>, comments: Vec<CommentView>,
posts: Vec<PostView>, posts: Vec<PostView>,
}
} }
``` ```
##### HTTP
`GET /user`
#### Save User Settings #### Save User Settings
##### Request ##### Request
```rust ```rust
@ -295,10 +389,16 @@ Only the first user will be able to be the admin.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "SaveUserSettings",
data: {
jwt: String jwt: String
}
} }
``` ```
##### HTTP
`PUT /save_user_settings`
#### Get Replies / Inbox #### Get Replies / Inbox
##### Request ##### Request
```rust ```rust
@ -316,10 +416,16 @@ Only the first user will be able to be the admin.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetReplies",
data: {
replies: Vec<ReplyView>, replies: Vec<ReplyView>,
}
} }
``` ```
##### HTTP
`GET /user/replies`
#### Get User Mentions #### Get User Mentions
##### Request ##### Request
@ -338,11 +444,42 @@ Only the first user will be able to be the admin.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetUserMentions",
data: {
mentions: Vec<UserMentionView>, mentions: Vec<UserMentionView>,
}
} }
``` ```
##### HTTP
`GET /user/mentions`
#### Edit User Mention
##### Request
```rust
{
op: "EditUserMention",
data: {
user_mention_id: i32,
read: Option<bool>,
auth: String,
}
}
```
##### Response
```rust
{
op: "EditUserMention",
data: {
mention: UserMentionView,
}
}
```
##### HTTP
`PUT /user/mention`
#### Mark All As Read #### Mark All As Read
Marks all user replies and mentions as read. Marks all user replies and mentions as read.
@ -359,11 +496,17 @@ Marks all user replies and mentions as read.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "MarkAllAsRead",
data: {
replies: Vec<ReplyView>, replies: Vec<ReplyView>,
}
} }
``` ```
##### HTTP
`POST /user/mark_all_as_read`
#### Delete Account #### Delete Account
*Permananently deletes your posts and comments* *Permananently deletes your posts and comments*
@ -381,11 +524,17 @@ Marks all user replies and mentions as read.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "DeleteAccount",
data: {
jwt: String, jwt: String,
}
} }
``` ```
##### HTTP
`POST /user/delete_account`
#### Add admin #### Add admin
##### Request ##### Request
```rust ```rust
@ -401,10 +550,15 @@ Marks all user replies and mentions as read.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "AddAdmin",
data: {
admins: Vec<UserView>, admins: Vec<UserView>,
}
} }
``` ```
##### HTTP
`POST /admin/add`
#### Ban user #### Ban user
##### Request ##### Request
@ -423,11 +577,16 @@ Marks all user replies and mentions as read.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "BanUser",
data: {
user: UserView, user: UserView,
banned: bool, banned: bool,
}
} }
``` ```
##### HTTP
`POST /user/ban`
### Site ### Site
#### List Categories #### List Categories
@ -440,13 +599,19 @@ Marks all user replies and mentions as read.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "ListCategories",
data: {
categories: Vec<Category> categories: Vec<Category>
}
} }
``` ```
##### HTTP
`GET /categories`
#### Search #### Search
Search types are `Both, Comments, Posts`.
Search types are `All, Comments, Posts, Communities, Users, Url`
##### Request ##### Request
```rust ```rust
@ -459,17 +624,26 @@ Search types are `Both, Comments, Posts`.
sort: String, sort: String,
page: Option<i64>, page: Option<i64>,
limit: Option<i64>, limit: Option<i64>,
auth?: Option<String>,
} }
} }
``` ```
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "Search",
data: {
type_: String,
comments: Vec<CommentView>, comments: Vec<CommentView>,
posts: Vec<PostView>, posts: Vec<PostView>,
communities: Vec<CommunityView>,
users: Vec<UserView>,
}
} }
``` ```
##### HTTP
`POST /search`
#### Get Modlog #### Get Modlog
##### Request ##### Request
@ -487,7 +661,8 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetModlog",
data: {
removed_posts: Vec<ModRemovePostView>, removed_posts: Vec<ModRemovePostView>,
locked_posts: Vec<ModLockPostView>, locked_posts: Vec<ModLockPostView>,
removed_comments: Vec<ModRemoveCommentView>, removed_comments: Vec<ModRemoveCommentView>,
@ -496,9 +671,14 @@ Search types are `Both, Comments, Posts`.
banned: Vec<ModBanView>, banned: Vec<ModBanView>,
added_to_community: Vec<ModAddCommunityView>, added_to_community: Vec<ModAddCommunityView>,
added: Vec<ModAddView>, added: Vec<ModAddView>,
}
} }
``` ```
##### HTTP
`GET /modlog`
#### Create Site #### Create Site
##### Request ##### Request
```rust ```rust
@ -514,11 +694,17 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "CreateSite",
data: {
site: SiteView, site: SiteView,
}
} }
``` ```
##### HTTP
`POST /site`
#### Edit Site #### Edit Site
##### Request ##### Request
```rust ```rust
@ -534,10 +720,15 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "EditSite",
data: {
site: SiteView, site: SiteView,
}
} }
``` ```
##### HTTP
`PUT /site`
#### Get Site #### Get Site
##### Request ##### Request
@ -549,12 +740,17 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetSite",
data: {
site: Option<SiteView>, site: Option<SiteView>,
admins: Vec<UserView>, admins: Vec<UserView>,
banned: Vec<UserView>, banned: Vec<UserView>,
}
} }
``` ```
##### HTTP
`GET /site`
#### Transfer Site #### Transfer Site
##### Request ##### Request
@ -570,12 +766,17 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "TransferSite",
data: {
site: Option<SiteView>, site: Option<SiteView>,
admins: Vec<UserView>, admins: Vec<UserView>,
banned: Vec<UserView>, banned: Vec<UserView>,
}
} }
``` ```
##### HTTP
`POST /site/transfer`
### Community ### Community
#### Get Community #### Get Community
@ -593,12 +794,17 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetCommunity",
data: {
community: CommunityView, community: CommunityView,
moderators: Vec<CommunityModeratorView>, moderators: Vec<CommunityModeratorView>,
admins: Vec<UserView>, admins: Vec<UserView>,
}
} }
``` ```
##### HTTP
`GET /community`
#### Create Community #### Create Community
##### Request ##### Request
@ -617,10 +823,15 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "CreateCommunity",
data: {
community: CommunityView community: CommunityView
}
} }
``` ```
##### HTTP
`POST /community`
#### List Communities #### List Communities
##### Request ##### Request
@ -638,10 +849,15 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "ListCommunities",
data: {
communities: Vec<CommunityView> communities: Vec<CommunityView>
}
} }
``` ```
##### HTTP
`GET /community/list`
#### Ban from Community #### Ban from Community
##### Request ##### Request
@ -661,11 +877,16 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "BanFromCommunity",
data: {
user: UserView, user: UserView,
banned: bool, banned: bool,
}
} }
``` ```
##### HTTP
`POST /community/ban_user`
#### Add Mod to Community #### Add Mod to Community
##### Request ##### Request
@ -683,10 +904,15 @@ Search types are `Both, Comments, Posts`.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "AddModToCommunity",
data: {
moderators: Vec<CommunityModeratorView>, moderators: Vec<CommunityModeratorView>,
}
} }
``` ```
##### HTTP
`POST /community/mod`
#### Edit Community #### Edit Community
Mods and admins can remove and lock a community, creators can delete it. Mods and admins can remove and lock a community, creators can delete it.
@ -712,10 +938,15 @@ Mods and admins can remove and lock a community, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "EditCommunity",
data: {
community: CommunityView community: CommunityView
}
} }
``` ```
##### HTTP
`PUT /community`
#### Follow Community #### Follow Community
##### Request ##### Request
@ -732,10 +963,15 @@ Mods and admins can remove and lock a community, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "FollowCommunity",
data: {
community: CommunityView community: CommunityView
}
} }
``` ```
##### HTTP
`POST /community/follow`
#### Get Followed Communities #### Get Followed Communities
##### Request ##### Request
@ -750,10 +986,15 @@ Mods and admins can remove and lock a community, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetFollowedCommunities",
data: {
communities: Vec<CommunityFollowerView> communities: Vec<CommunityFollowerView>
}
} }
``` ```
##### HTTP
`GET /user/followed_communities`
#### Transfer Community #### Transfer Community
##### Request ##### Request
@ -770,12 +1011,17 @@ Mods and admins can remove and lock a community, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "TransferCommunity",
data: {
community: CommunityView, community: CommunityView,
moderators: Vec<CommunityModeratorView>, moderators: Vec<CommunityModeratorView>,
admins: Vec<UserView>, admins: Vec<UserView>,
}
} }
``` ```
##### HTTP
`POST /community/transfer`
### Post ### Post
#### Create Post #### Create Post
@ -795,10 +1041,15 @@ Mods and admins can remove and lock a community, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "CreatePost",
data: {
post: PostView post: PostView
}
} }
``` ```
##### HTTP
`POST /post`
#### Get Post #### Get Post
##### Request ##### Request
@ -814,16 +1065,22 @@ Mods and admins can remove and lock a community, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetPost",
data: {
post: PostView, post: PostView,
comments: Vec<CommentView>, comments: Vec<CommentView>,
community: CommunityView, community: CommunityView,
moderators: Vec<CommunityModeratorView>, moderators: Vec<CommunityModeratorView>,
admins: Vec<UserView>, admins: Vec<UserView>,
}
} }
``` ```
##### HTTP
`GET /post`
#### Get Posts #### Get Posts
Post listing types are `All, Subscribed, Community` Post listing types are `All, Subscribed, Community`
##### Request ##### Request
@ -843,12 +1100,18 @@ Post listing types are `All, Subscribed, Community`
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "GetPosts",
data: {
posts: Vec<PostView>, posts: Vec<PostView>,
}
} }
``` ```
##### HTTP
`GET /post/list`
#### Create Post Like #### Create Post Like
`score` can be 0, -1, or 1 `score` can be 0, -1, or 1
##### Request ##### Request
@ -865,12 +1128,18 @@ Post listing types are `All, Subscribed, Community`
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "CreatePostLike",
data: {
post: PostView post: PostView
}
} }
``` ```
##### HTTP
`POST /post/like`
#### Edit Post #### Edit Post
Mods and admins can remove and lock a post, creators can delete it. Mods and admins can remove and lock a post, creators can delete it.
##### Request ##### Request
@ -895,11 +1164,17 @@ Mods and admins can remove and lock a post, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "EditPost",
data: {
post: PostView post: PostView
}
} }
``` ```
##### HTTP
`PUT /post`
#### Save Post #### Save Post
##### Request ##### Request
```rust ```rust
@ -915,10 +1190,15 @@ Mods and admins can remove and lock a post, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "SavePost",
data: {
post: PostView post: PostView
}
} }
``` ```
##### HTTP
`POST /post/save`
### Comment ### Comment
#### Create Comment #### Create Comment
@ -938,12 +1218,19 @@ Mods and admins can remove and lock a post, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "CreateComment",
data: {
comment: CommentView comment: CommentView
}
} }
``` ```
##### HTTP
`POST /comment`
#### Edit Comment #### Edit Comment
Mods and admins can remove a comment, creators can delete it. Mods and admins can remove a comment, creators can delete it.
##### Request ##### Request
@ -967,10 +1254,15 @@ Mods and admins can remove a comment, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "EditComment",
data: {
comment: CommentView comment: CommentView
}
} }
``` ```
##### HTTP
`PUT /comment`
#### Save Comment #### Save Comment
##### Request ##### Request
@ -987,12 +1279,18 @@ Mods and admins can remove a comment, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "SaveComment",
data: {
comment: CommentView comment: CommentView
}
} }
``` ```
##### HTTP
`POST /comment/save`
#### Create Comment Like #### Create Comment Like
`score` can be 0, -1, or 1 `score` can be 0, -1, or 1
##### Request ##### Request
@ -1010,10 +1308,15 @@ Mods and admins can remove a comment, creators can delete it.
##### Response ##### Response
```rust ```rust
{ {
op: String, op: "CreateCommentLike",
data: {
comment: CommentView comment: CommentView
}
} }
``` ```
##### HTTP
`POST /comment/like`
### RSS / Atom feeds ### RSS / Atom feeds

View file

@ -35,7 +35,6 @@ pub struct SaveComment {
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct CommentResponse { pub struct CommentResponse {
op: String,
pub comment: CommentView, pub comment: CommentView,
} }
@ -53,7 +52,7 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -63,12 +62,12 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
// Check for a community ban // Check for a community ban
let post = Post::read(&conn, data.post_id)?; let post = Post::read(&conn, data.post_id)?;
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
return Err(APIError::err(&self.op, "community_ban").into()); return Err(APIError::err("community_ban").into());
} }
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
let content_slurs_removed = remove_slurs(&data.content.to_owned()); let content_slurs_removed = remove_slurs(&data.content.to_owned());
@ -86,7 +85,7 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
let inserted_comment = match Comment::create(&conn, &comment_form) { let inserted_comment = match Comment::create(&conn, &comment_form) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_create_comment").into()), Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
}; };
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
@ -193,13 +192,12 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
let _inserted_like = match CommentLike::like(&conn, &like_form) { let _inserted_like = match CommentLike::like(&conn, &like_form) {
Ok(like) => like, Ok(like) => like,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_comment").into()), Err(_e) => return Err(APIError::err("couldnt_like_comment").into()),
}; };
let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?; let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?;
Ok(CommentResponse { Ok(CommentResponse {
op: self.op.to_string(),
comment: comment_view, comment: comment_view,
}) })
} }
@ -211,7 +209,7 @@ impl Perform<CommentResponse> for Oper<EditComment> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -231,17 +229,17 @@ impl Perform<CommentResponse> for Oper<EditComment> {
editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect()); editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
if !editors.contains(&user_id) { if !editors.contains(&user_id) {
return Err(APIError::err(&self.op, "no_comment_edit_allowed").into()); return Err(APIError::err("no_comment_edit_allowed").into());
} }
// Check for a community ban // Check for a community ban
if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() { if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() {
return Err(APIError::err(&self.op, "community_ban").into()); return Err(APIError::err("community_ban").into());
} }
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
} }
@ -264,7 +262,7 @@ impl Perform<CommentResponse> for Oper<EditComment> {
let _updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) { let _updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
}; };
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
@ -310,7 +308,6 @@ impl Perform<CommentResponse> for Oper<EditComment> {
let comment_view = CommentView::read(&conn, data.edit_id, Some(user_id))?; let comment_view = CommentView::read(&conn, data.edit_id, Some(user_id))?;
Ok(CommentResponse { Ok(CommentResponse {
op: self.op.to_string(),
comment: comment_view, comment: comment_view,
}) })
} }
@ -322,7 +319,7 @@ impl Perform<CommentResponse> for Oper<SaveComment> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -335,19 +332,18 @@ impl Perform<CommentResponse> for Oper<SaveComment> {
if data.save { if data.save {
match CommentSaved::save(&conn, &comment_saved_form) { match CommentSaved::save(&conn, &comment_saved_form) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_comment").into()), Err(_e) => return Err(APIError::err("couldnt_save_comment").into()),
}; };
} else { } else {
match CommentSaved::unsave(&conn, &comment_saved_form) { match CommentSaved::unsave(&conn, &comment_saved_form) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_comment").into()), Err(_e) => return Err(APIError::err("couldnt_save_comment").into()),
}; };
} }
let comment_view = CommentView::read(&conn, data.comment_id, Some(user_id))?; let comment_view = CommentView::read(&conn, data.comment_id, Some(user_id))?;
Ok(CommentResponse { Ok(CommentResponse {
op: self.op.to_string(),
comment: comment_view, comment: comment_view,
}) })
} }
@ -359,7 +355,7 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -368,19 +364,19 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
if data.score == -1 { if data.score == -1 {
let site = SiteView::read(&conn)?; let site = SiteView::read(&conn)?;
if !site.enable_downvotes { if !site.enable_downvotes {
return Err(APIError::err(&self.op, "downvotes_disabled").into()); return Err(APIError::err("downvotes_disabled").into());
} }
} }
// Check for a community ban // Check for a community ban
let post = Post::read(&conn, data.post_id)?; let post = Post::read(&conn, data.post_id)?;
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
return Err(APIError::err(&self.op, "community_ban").into()); return Err(APIError::err("community_ban").into());
} }
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
@ -398,7 +394,7 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
if do_add { if do_add {
let _inserted_like = match CommentLike::like(&conn, &like_form) { let _inserted_like = match CommentLike::like(&conn, &like_form) {
Ok(like) => like, Ok(like) => like,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_comment").into()), Err(_e) => return Err(APIError::err("couldnt_like_comment").into()),
}; };
} }
@ -406,7 +402,6 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
let liked_comment = CommentView::read(&conn, data.comment_id, Some(user_id))?; let liked_comment = CommentView::read(&conn, data.comment_id, Some(user_id))?;
Ok(CommentResponse { Ok(CommentResponse {
op: self.op.to_string(),
comment: liked_comment, comment: liked_comment,
}) })
} }

View file

@ -11,7 +11,6 @@ pub struct GetCommunity {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetCommunityResponse { pub struct GetCommunityResponse {
op: String,
community: CommunityView, community: CommunityView,
moderators: Vec<CommunityModeratorView>, moderators: Vec<CommunityModeratorView>,
admins: Vec<UserView>, admins: Vec<UserView>,
@ -29,7 +28,6 @@ pub struct CreateCommunity {
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct CommunityResponse { pub struct CommunityResponse {
op: String,
pub community: CommunityView, pub community: CommunityView,
} }
@ -43,7 +41,6 @@ pub struct ListCommunities {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ListCommunitiesResponse { pub struct ListCommunitiesResponse {
op: String,
communities: Vec<CommunityView>, communities: Vec<CommunityView>,
} }
@ -59,7 +56,6 @@ pub struct BanFromCommunity {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct BanFromCommunityResponse { pub struct BanFromCommunityResponse {
op: String,
user: UserView, user: UserView,
banned: bool, banned: bool,
} }
@ -74,7 +70,6 @@ pub struct AddModToCommunity {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct AddModToCommunityResponse { pub struct AddModToCommunityResponse {
op: String,
moderators: Vec<CommunityModeratorView>, moderators: Vec<CommunityModeratorView>,
} }
@ -107,7 +102,6 @@ pub struct GetFollowedCommunities {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetFollowedCommunitiesResponse { pub struct GetFollowedCommunitiesResponse {
op: String,
communities: Vec<CommunityFollowerView>, communities: Vec<CommunityFollowerView>,
} }
@ -141,19 +135,19 @@ impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
data.name.to_owned().unwrap_or_else(|| "main".to_string()), data.name.to_owned().unwrap_or_else(|| "main".to_string()),
) { ) {
Ok(community) => community.id, Ok(community) => community.id,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
} }
} }
}; };
let community_view = match CommunityView::read(&conn, community_id, user_id) { let community_view = match CommunityView::read(&conn, community_id, user_id) {
Ok(community) => community, Ok(community) => community,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
}; };
let moderators = match CommunityModeratorView::for_community(&conn, community_id) { let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
Ok(moderators) => moderators, Ok(moderators) => moderators,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
}; };
let site_creator_id = Site::read(&conn, 1)?.creator_id; let site_creator_id = Site::read(&conn, 1)?.creator_id;
@ -164,7 +158,6 @@ impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
// Return the jwt // Return the jwt
Ok(GetCommunityResponse { Ok(GetCommunityResponse {
op: self.op.to_string(),
community: community_view, community: community_view,
moderators, moderators,
admins, admins,
@ -178,21 +171,21 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
if has_slurs(&data.name) if has_slurs(&data.name)
|| has_slurs(&data.title) || has_slurs(&data.title)
|| (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
{ {
return Err(APIError::err(&self.op, "no_slurs").into()); return Err(APIError::err("no_slurs").into());
} }
let user_id = claims.id; let user_id = claims.id;
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
// When you create a community, make sure the user becomes a moderator and a follower // When you create a community, make sure the user becomes a moderator and a follower
@ -210,7 +203,7 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
let inserted_community = match Community::create(&conn, &community_form) { let inserted_community = match Community::create(&conn, &community_form) {
Ok(community) => community, Ok(community) => community,
Err(_e) => return Err(APIError::err(&self.op, "community_already_exists").into()), Err(_e) => return Err(APIError::err("community_already_exists").into()),
}; };
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
@ -221,9 +214,7 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
let _inserted_community_moderator = let _inserted_community_moderator =
match CommunityModerator::join(&conn, &community_moderator_form) { match CommunityModerator::join(&conn, &community_moderator_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => { Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
}
}; };
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
@ -234,13 +225,12 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
let _inserted_community_follower = let _inserted_community_follower =
match CommunityFollower::follow(&conn, &community_follower_form) { match CommunityFollower::follow(&conn, &community_follower_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
}; };
let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?; let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
Ok(CommunityResponse { Ok(CommunityResponse {
op: self.op.to_string(),
community: community_view, community: community_view,
}) })
} }
@ -251,19 +241,19 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
let data: &EditCommunity = &self.data; let data: &EditCommunity = &self.data;
if has_slurs(&data.name) || has_slurs(&data.title) { if has_slurs(&data.name) || has_slurs(&data.title) {
return Err(APIError::err(&self.op, "no_slurs").into()); return Err(APIError::err("no_slurs").into());
} }
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
// Verify its a mod // Verify its a mod
@ -276,7 +266,7 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
); );
editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect()); editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
if !editors.contains(&user_id) { if !editors.contains(&user_id) {
return Err(APIError::err(&self.op, "no_community_edit_allowed").into()); return Err(APIError::err("no_community_edit_allowed").into());
} }
let community_form = CommunityForm { let community_form = CommunityForm {
@ -293,7 +283,7 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
let _updated_community = match Community::update(&conn, data.edit_id, &community_form) { let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
Ok(community) => community, Ok(community) => community,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()), Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
}; };
// Mod tables // Mod tables
@ -315,7 +305,6 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?; let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
Ok(CommunityResponse { Ok(CommunityResponse {
op: self.op.to_string(),
community: community_view, community: community_view,
}) })
} }
@ -354,10 +343,7 @@ impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
.list()?; .list()?;
// Return the jwt // Return the jwt
Ok(ListCommunitiesResponse { Ok(ListCommunitiesResponse { communities })
op: self.op.to_string(),
communities,
})
} }
} }
@ -367,7 +353,7 @@ impl Perform<CommunityResponse> for Oper<FollowCommunity> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -380,19 +366,18 @@ impl Perform<CommunityResponse> for Oper<FollowCommunity> {
if data.follow { if data.follow {
match CommunityFollower::follow(&conn, &community_follower_form) { match CommunityFollower::follow(&conn, &community_follower_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
}; };
} else { } else {
match CommunityFollower::ignore(&conn, &community_follower_form) { match CommunityFollower::ignore(&conn, &community_follower_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
}; };
} }
let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?; let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
Ok(CommunityResponse { Ok(CommunityResponse {
op: self.op.to_string(),
community: community_view, community: community_view,
}) })
} }
@ -404,7 +389,7 @@ impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -412,14 +397,11 @@ impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
let communities: Vec<CommunityFollowerView> = let communities: Vec<CommunityFollowerView> =
match CommunityFollowerView::for_user(&conn, user_id) { match CommunityFollowerView::for_user(&conn, user_id) {
Ok(communities) => communities, Ok(communities) => communities,
Err(_e) => return Err(APIError::err(&self.op, "system_err_login").into()), Err(_e) => return Err(APIError::err("system_err_login").into()),
}; };
// Return the jwt // Return the jwt
Ok(GetFollowedCommunitiesResponse { Ok(GetFollowedCommunitiesResponse { communities })
op: self.op.to_string(),
communities,
})
} }
} }
@ -429,7 +411,7 @@ impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -442,12 +424,12 @@ impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
if data.ban { if data.ban {
match CommunityUserBan::ban(&conn, &community_user_ban_form) { match CommunityUserBan::ban(&conn, &community_user_ban_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()), Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
}; };
} else { } else {
match CommunityUserBan::unban(&conn, &community_user_ban_form) { match CommunityUserBan::unban(&conn, &community_user_ban_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()), Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
}; };
} }
@ -470,7 +452,6 @@ impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
let user_view = UserView::read(&conn, data.user_id)?; let user_view = UserView::read(&conn, data.user_id)?;
Ok(BanFromCommunityResponse { Ok(BanFromCommunityResponse {
op: self.op.to_string(),
user: user_view, user: user_view,
banned: data.ban, banned: data.ban,
}) })
@ -483,7 +464,7 @@ impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -496,16 +477,12 @@ impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
if data.added { if data.added {
match CommunityModerator::join(&conn, &community_moderator_form) { match CommunityModerator::join(&conn, &community_moderator_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => { Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
}
}; };
} else { } else {
match CommunityModerator::leave(&conn, &community_moderator_form) { match CommunityModerator::leave(&conn, &community_moderator_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => { Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
}
}; };
} }
@ -520,10 +497,7 @@ impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?; let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
Ok(AddModToCommunityResponse { Ok(AddModToCommunityResponse { moderators })
op: self.op.to_string(),
moderators,
})
} }
} }
@ -533,7 +507,7 @@ impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -548,7 +522,7 @@ impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
// Make sure user is the creator, or an admin // Make sure user is the creator, or an admin
if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) { if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
return Err(APIError::err(&self.op, "not_an_admin").into()); return Err(APIError::err("not_an_admin").into());
} }
let community_form = CommunityForm { let community_form = CommunityForm {
@ -565,7 +539,7 @@ impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
let _updated_community = match Community::update(&conn, data.community_id, &community_form) { let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
Ok(community) => community, Ok(community) => community,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()), Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
}; };
// You also have to re-do the community_moderator table, reordering it. // You also have to re-do the community_moderator table, reordering it.
@ -588,9 +562,7 @@ impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
let _inserted_community_moderator = let _inserted_community_moderator =
match CommunityModerator::join(&conn, &community_moderator_form) { match CommunityModerator::join(&conn, &community_moderator_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => { Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
}
}; };
} }
@ -605,17 +577,16 @@ impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) { let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
Ok(community) => community, Ok(community) => community,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
}; };
let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) { let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
Ok(moderators) => moderators, Ok(moderators) => moderators,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()), Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
}; };
// Return the jwt // Return the jwt
Ok(GetCommunityResponse { Ok(GetCommunityResponse {
op: self.op.to_string(),
community: community_view, community: community_view,
moderators, moderators,
admins, admins,

View file

@ -28,76 +28,27 @@ pub mod post;
pub mod site; pub mod site;
pub mod user; pub mod user;
#[derive(EnumString, ToString, Debug)]
pub enum UserOperation {
Login,
Register,
CreateCommunity,
CreatePost,
ListCommunities,
ListCategories,
GetPost,
GetCommunity,
CreateComment,
EditComment,
SaveComment,
CreateCommentLike,
GetPosts,
CreatePostLike,
EditPost,
SavePost,
EditCommunity,
FollowCommunity,
GetFollowedCommunities,
GetUserDetails,
GetReplies,
GetUserMentions,
EditUserMention,
GetModlog,
BanFromCommunity,
AddModToCommunity,
CreateSite,
EditSite,
GetSite,
AddAdmin,
BanUser,
Search,
MarkAllAsRead,
SaveUserSettings,
TransferCommunity,
TransferSite,
DeleteAccount,
PasswordReset,
PasswordChange,
CreatePrivateMessage,
EditPrivateMessage,
GetPrivateMessages,
}
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
#[fail(display = "{{\"op\":\"{}\", \"error\":\"{}\"}}", op, message)] #[fail(display = "{{\"error\":\"{}\"}}", message)]
pub struct APIError { pub struct APIError {
pub op: String,
pub message: String, pub message: String,
} }
impl APIError { impl APIError {
pub fn err(op: &UserOperation, msg: &str) -> Self { pub fn err(msg: &str) -> Self {
APIError { APIError {
op: op.to_string(),
message: msg.to_string(), message: msg.to_string(),
} }
} }
} }
pub struct Oper<T> { pub struct Oper<T> {
op: UserOperation,
data: T, data: T,
} }
impl<T> Oper<T> { impl<T> Oper<T> {
pub fn new(op: UserOperation, data: T) -> Oper<T> { pub fn new(data: T) -> Oper<T> {
Oper { op, data } Oper { data }
} }
} }

View file

@ -14,7 +14,6 @@ pub struct CreatePost {
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct PostResponse { pub struct PostResponse {
op: String,
pub post: PostView, pub post: PostView,
} }
@ -26,7 +25,6 @@ pub struct GetPost {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetPostResponse { pub struct GetPostResponse {
op: String,
post: PostView, post: PostView,
comments: Vec<CommentView>, comments: Vec<CommentView>,
community: CommunityView, community: CommunityView,
@ -46,7 +44,6 @@ pub struct GetPosts {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetPostsResponse { pub struct GetPostsResponse {
op: String,
posts: Vec<PostView>, posts: Vec<PostView>,
} }
@ -59,7 +56,6 @@ pub struct CreatePostLike {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct CreatePostLikeResponse { pub struct CreatePostLikeResponse {
op: String,
post: PostView, post: PostView,
} }
@ -93,23 +89,23 @@ impl Perform<PostResponse> for Oper<CreatePost> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) { if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
return Err(APIError::err(&self.op, "no_slurs").into()); return Err(APIError::err("no_slurs").into());
} }
let user_id = claims.id; let user_id = claims.id;
// Check for a community ban // Check for a community ban
if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() { if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
return Err(APIError::err(&self.op, "community_ban").into()); return Err(APIError::err("community_ban").into());
} }
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
let post_form = PostForm { let post_form = PostForm {
@ -128,7 +124,7 @@ impl Perform<PostResponse> for Oper<CreatePost> {
let inserted_post = match Post::create(&conn, &post_form) { let inserted_post = match Post::create(&conn, &post_form) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_create_post").into()), Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
}; };
// They like their own post by default // They like their own post by default
@ -141,19 +137,16 @@ impl Perform<PostResponse> for Oper<CreatePost> {
// Only add the like if the score isnt 0 // Only add the like if the score isnt 0
let _inserted_like = match PostLike::like(&conn, &like_form) { let _inserted_like = match PostLike::like(&conn, &like_form) {
Ok(like) => like, Ok(like) => like,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_post").into()), Err(_e) => return Err(APIError::err("couldnt_like_post").into()),
}; };
// Refetch the view // Refetch the view
let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) { let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_post").into()), Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
}; };
Ok(PostResponse { Ok(PostResponse { post: post_view })
op: self.op.to_string(),
post: post_view,
})
} }
} }
@ -174,7 +167,7 @@ impl Perform<GetPostResponse> for Oper<GetPost> {
let post_view = match PostView::read(&conn, data.id, user_id) { let post_view = match PostView::read(&conn, data.id, user_id) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_post").into()), Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
}; };
let comments = CommentQueryBuilder::create(&conn) let comments = CommentQueryBuilder::create(&conn)
@ -195,7 +188,6 @@ impl Perform<GetPostResponse> for Oper<GetPost> {
// Return the jwt // Return the jwt
Ok(GetPostResponse { Ok(GetPostResponse {
op: self.op.to_string(),
post: post_view, post: post_view,
comments, comments,
community, community,
@ -241,13 +233,10 @@ impl Perform<GetPostsResponse> for Oper<GetPosts> {
.list() .list()
{ {
Ok(posts) => posts, Ok(posts) => posts,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_get_posts").into()), Err(_e) => return Err(APIError::err("couldnt_get_posts").into()),
}; };
Ok(GetPostsResponse { Ok(GetPostsResponse { posts })
op: self.op.to_string(),
posts,
})
} }
} }
@ -257,7 +246,7 @@ impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -266,19 +255,19 @@ impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
if data.score == -1 { if data.score == -1 {
let site = SiteView::read(&conn)?; let site = SiteView::read(&conn)?;
if !site.enable_downvotes { if !site.enable_downvotes {
return Err(APIError::err(&self.op, "downvotes_disabled").into()); return Err(APIError::err("downvotes_disabled").into());
} }
} }
// Check for a community ban // Check for a community ban
let post = Post::read(&conn, data.post_id)?; let post = Post::read(&conn, data.post_id)?;
if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
return Err(APIError::err(&self.op, "community_ban").into()); return Err(APIError::err("community_ban").into());
} }
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
let like_form = PostLikeForm { let like_form = PostLikeForm {
@ -295,20 +284,17 @@ impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
if do_add { if do_add {
let _inserted_like = match PostLike::like(&conn, &like_form) { let _inserted_like = match PostLike::like(&conn, &like_form) {
Ok(like) => like, Ok(like) => like,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_like_post").into()), Err(_e) => return Err(APIError::err("couldnt_like_post").into()),
}; };
} }
let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) { let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_post").into()), Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
}; };
// just output the score // just output the score
Ok(CreatePostLikeResponse { Ok(CreatePostLikeResponse { post: post_view })
op: self.op.to_string(),
post: post_view,
})
} }
} }
@ -316,12 +302,12 @@ impl Perform<PostResponse> for Oper<EditPost> {
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> { fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
let data: &EditPost = &self.data; let data: &EditPost = &self.data;
if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) { if has_slurs(&data.name) || (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
return Err(APIError::err(&self.op, "no_slurs").into()); return Err(APIError::err("no_slurs").into());
} }
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -336,17 +322,17 @@ impl Perform<PostResponse> for Oper<EditPost> {
); );
editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect()); editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
if !editors.contains(&user_id) { if !editors.contains(&user_id) {
return Err(APIError::err(&self.op, "no_post_edit_allowed").into()); return Err(APIError::err("no_post_edit_allowed").into());
} }
// Check for a community ban // Check for a community ban
if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() { if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
return Err(APIError::err(&self.op, "community_ban").into()); return Err(APIError::err("community_ban").into());
} }
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
let post_form = PostForm { let post_form = PostForm {
@ -365,7 +351,7 @@ impl Perform<PostResponse> for Oper<EditPost> {
let _updated_post = match Post::update(&conn, data.edit_id, &post_form) { let _updated_post = match Post::update(&conn, data.edit_id, &post_form) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_post").into()), Err(_e) => return Err(APIError::err("couldnt_update_post").into()),
}; };
// Mod tables // Mod tables
@ -399,10 +385,7 @@ impl Perform<PostResponse> for Oper<EditPost> {
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?; let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
Ok(PostResponse { Ok(PostResponse { post: post_view })
op: self.op.to_string(),
post: post_view,
})
} }
} }
@ -412,7 +395,7 @@ impl Perform<PostResponse> for Oper<SavePost> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -425,20 +408,17 @@ impl Perform<PostResponse> for Oper<SavePost> {
if data.save { if data.save {
match PostSaved::save(&conn, &post_saved_form) { match PostSaved::save(&conn, &post_saved_form) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_post").into()), Err(_e) => return Err(APIError::err("couldnt_save_post").into()),
}; };
} else { } else {
match PostSaved::unsave(&conn, &post_saved_form) { match PostSaved::unsave(&conn, &post_saved_form) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_save_post").into()), Err(_e) => return Err(APIError::err("couldnt_save_post").into()),
}; };
} }
let post_view = PostView::read(&conn, data.post_id, Some(user_id))?; let post_view = PostView::read(&conn, data.post_id, Some(user_id))?;
Ok(PostResponse { Ok(PostResponse { post: post_view })
op: self.op.to_string(),
post: post_view,
})
} }
} }

View file

@ -7,7 +7,6 @@ pub struct ListCategories;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ListCategoriesResponse { pub struct ListCategoriesResponse {
op: String,
categories: Vec<Category>, categories: Vec<Category>,
} }
@ -24,7 +23,6 @@ pub struct Search {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct SearchResponse { pub struct SearchResponse {
op: String,
type_: String, type_: String,
comments: Vec<CommentView>, comments: Vec<CommentView>,
posts: Vec<PostView>, posts: Vec<PostView>,
@ -42,7 +40,6 @@ pub struct GetModlog {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetModlogResponse { pub struct GetModlogResponse {
op: String,
removed_posts: Vec<ModRemovePostView>, removed_posts: Vec<ModRemovePostView>,
locked_posts: Vec<ModLockPostView>, locked_posts: Vec<ModLockPostView>,
stickied_posts: Vec<ModStickyPostView>, stickied_posts: Vec<ModStickyPostView>,
@ -79,13 +76,11 @@ pub struct GetSite;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct SiteResponse { pub struct SiteResponse {
op: String,
site: SiteView, site: SiteView,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetSiteResponse { pub struct GetSiteResponse {
op: String,
site: Option<SiteView>, site: Option<SiteView>,
admins: Vec<UserView>, admins: Vec<UserView>,
banned: Vec<UserView>, banned: Vec<UserView>,
@ -105,10 +100,7 @@ impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
let categories: Vec<Category> = Category::list_all(&conn)?; let categories: Vec<Category> = Category::list_all(&conn)?;
// Return the jwt // Return the jwt
Ok(ListCategoriesResponse { Ok(ListCategoriesResponse { categories })
op: self.op.to_string(),
categories,
})
} }
} }
@ -172,7 +164,6 @@ impl Perform<GetModlogResponse> for Oper<GetModlog> {
// Return the jwt // Return the jwt
Ok(GetModlogResponse { Ok(GetModlogResponse {
op: self.op.to_string(),
removed_posts, removed_posts,
locked_posts, locked_posts,
stickied_posts, stickied_posts,
@ -192,20 +183,20 @@ impl Perform<SiteResponse> for Oper<CreateSite> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
if has_slurs(&data.name) if has_slurs(&data.name)
|| (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
{ {
return Err(APIError::err(&self.op, "no_slurs").into()); return Err(APIError::err("no_slurs").into());
} }
let user_id = claims.id; let user_id = claims.id;
// Make sure user is an admin // Make sure user is an admin
if !UserView::read(&conn, user_id)?.admin { if !UserView::read(&conn, user_id)?.admin {
return Err(APIError::err(&self.op, "not_an_admin").into()); return Err(APIError::err("not_an_admin").into());
} }
let site_form = SiteForm { let site_form = SiteForm {
@ -220,15 +211,12 @@ impl Perform<SiteResponse> for Oper<CreateSite> {
match Site::create(&conn, &site_form) { match Site::create(&conn, &site_form) {
Ok(site) => site, Ok(site) => site,
Err(_e) => return Err(APIError::err(&self.op, "site_already_exists").into()), Err(_e) => return Err(APIError::err("site_already_exists").into()),
}; };
let site_view = SiteView::read(&conn)?; let site_view = SiteView::read(&conn)?;
Ok(SiteResponse { Ok(SiteResponse { site: site_view })
op: self.op.to_string(),
site: site_view,
})
} }
} }
@ -238,20 +226,20 @@ impl Perform<SiteResponse> for Oper<EditSite> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
if has_slurs(&data.name) if has_slurs(&data.name)
|| (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
{ {
return Err(APIError::err(&self.op, "no_slurs").into()); return Err(APIError::err("no_slurs").into());
} }
let user_id = claims.id; let user_id = claims.id;
// Make sure user is an admin // Make sure user is an admin
if !UserView::read(&conn, user_id)?.admin { if !UserView::read(&conn, user_id)?.admin {
return Err(APIError::err(&self.op, "not_an_admin").into()); return Err(APIError::err("not_an_admin").into());
} }
let found_site = Site::read(&conn, 1)?; let found_site = Site::read(&conn, 1)?;
@ -268,15 +256,12 @@ impl Perform<SiteResponse> for Oper<EditSite> {
match Site::update(&conn, 1, &site_form) { match Site::update(&conn, 1, &site_form) {
Ok(site) => site, Ok(site) => site,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_site").into()), Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
}; };
let site_view = SiteView::read(&conn)?; let site_view = SiteView::read(&conn)?;
Ok(SiteResponse { Ok(SiteResponse { site: site_view })
op: self.op.to_string(),
site: site_view,
})
} }
} }
@ -301,7 +286,6 @@ impl Perform<GetSiteResponse> for Oper<GetSite> {
let banned = UserView::banned(&conn)?; let banned = UserView::banned(&conn)?;
Ok(GetSiteResponse { Ok(GetSiteResponse {
op: self.op.to_string(),
site: site_view, site: site_view,
admins, admins,
banned, banned,
@ -419,7 +403,6 @@ impl Perform<SearchResponse> for Oper<Search> {
// Return the jwt // Return the jwt
Ok(SearchResponse { Ok(SearchResponse {
op: self.op.to_string(),
type_: data.type_.to_owned(), type_: data.type_.to_owned(),
comments, comments,
posts, posts,
@ -435,7 +418,7 @@ impl Perform<GetSiteResponse> for Oper<TransferSite> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -444,7 +427,7 @@ impl Perform<GetSiteResponse> for Oper<TransferSite> {
// Make sure user is the creator // Make sure user is the creator
if read_site.creator_id != user_id { if read_site.creator_id != user_id {
return Err(APIError::err(&self.op, "not_an_admin").into()); return Err(APIError::err("not_an_admin").into());
} }
let site_form = SiteForm { let site_form = SiteForm {
@ -459,7 +442,7 @@ impl Perform<GetSiteResponse> for Oper<TransferSite> {
match Site::update(&conn, 1, &site_form) { match Site::update(&conn, 1, &site_form) {
Ok(site) => site, Ok(site) => site,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_site").into()), Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
}; };
// Mod tables // Mod tables
@ -484,7 +467,6 @@ impl Perform<GetSiteResponse> for Oper<TransferSite> {
let banned = UserView::banned(&conn)?; let banned = UserView::banned(&conn)?;
Ok(GetSiteResponse { Ok(GetSiteResponse {
op: self.op.to_string(),
site: Some(site_view), site: Some(site_view),
admins, admins,
banned, banned,

View file

@ -41,7 +41,6 @@ pub struct SaveUserSettings {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct LoginResponse { pub struct LoginResponse {
op: String,
jwt: String, jwt: String,
} }
@ -59,7 +58,6 @@ pub struct GetUserDetails {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetUserDetailsResponse { pub struct GetUserDetailsResponse {
op: String,
user: UserView, user: UserView,
follows: Vec<CommunityFollowerView>, follows: Vec<CommunityFollowerView>,
moderates: Vec<CommunityModeratorView>, moderates: Vec<CommunityModeratorView>,
@ -70,13 +68,11 @@ pub struct GetUserDetailsResponse {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetRepliesResponse { pub struct GetRepliesResponse {
op: String,
replies: Vec<ReplyView>, replies: Vec<ReplyView>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetUserMentionsResponse { pub struct GetUserMentionsResponse {
op: String,
mentions: Vec<UserMentionView>, mentions: Vec<UserMentionView>,
} }
@ -94,7 +90,6 @@ pub struct AddAdmin {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct AddAdminResponse { pub struct AddAdminResponse {
op: String,
admins: Vec<UserView>, admins: Vec<UserView>,
} }
@ -109,7 +104,6 @@ pub struct BanUser {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct BanUserResponse { pub struct BanUserResponse {
op: String,
user: UserView, user: UserView,
banned: bool, banned: bool,
} }
@ -141,7 +135,6 @@ pub struct EditUserMention {
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct UserMentionResponse { pub struct UserMentionResponse {
op: String,
mention: UserMentionView, mention: UserMentionView,
} }
@ -157,9 +150,7 @@ pub struct PasswordReset {
} }
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct PasswordResetResponse { pub struct PasswordResetResponse {}
op: String,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct PasswordChange { pub struct PasswordChange {
@ -194,13 +185,11 @@ pub struct GetPrivateMessages {
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct PrivateMessagesResponse { pub struct PrivateMessagesResponse {
op: String,
messages: Vec<PrivateMessageView>, messages: Vec<PrivateMessageView>,
} }
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
pub struct PrivateMessageResponse { pub struct PrivateMessageResponse {
op: String,
message: PrivateMessageView, message: PrivateMessageView,
} }
@ -211,20 +200,17 @@ impl Perform<LoginResponse> for Oper<Login> {
// Fetch that username / email // Fetch that username / email
let user: User_ = match User_::find_by_email_or_username(&conn, &data.username_or_email) { let user: User_ = match User_::find_by_email_or_username(&conn, &data.username_or_email) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_that_username_or_email").into()), Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
}; };
// Verify the password // Verify the password
let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false); let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
if !valid { if !valid {
return Err(APIError::err(&self.op, "password_incorrect").into()); return Err(APIError::err("password_incorrect").into());
} }
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse { jwt: user.jwt() })
op: self.op.to_string(),
jwt: user.jwt(),
})
} }
} }
@ -235,22 +221,22 @@ impl Perform<LoginResponse> for Oper<Register> {
// Make sure site has open registration // Make sure site has open registration
if let Ok(site) = SiteView::read(&conn) { if let Ok(site) = SiteView::read(&conn) {
if !site.open_registration { if !site.open_registration {
return Err(APIError::err(&self.op, "registration_closed").into()); return Err(APIError::err("registration_closed").into());
} }
} }
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
return Err(APIError::err(&self.op, "passwords_dont_match").into()); return Err(APIError::err("passwords_dont_match").into());
} }
if has_slurs(&data.username) { if has_slurs(&data.username) {
return Err(APIError::err(&self.op, "no_slurs").into()); return Err(APIError::err("no_slurs").into());
} }
// Make sure there are no admins // Make sure there are no admins
if data.admin && !UserView::admins(&conn)?.is_empty() { if data.admin && !UserView::admins(&conn)?.is_empty() {
return Err(APIError::err(&self.op, "admin_already_created").into()); return Err(APIError::err("admin_already_created").into());
} }
// Register the new user // Register the new user
@ -286,7 +272,7 @@ impl Perform<LoginResponse> for Oper<Register> {
"user_already_exists" "user_already_exists"
}; };
return Err(APIError::err(&self.op, err_type).into()); return Err(APIError::err(err_type).into());
} }
}; };
@ -318,7 +304,7 @@ impl Perform<LoginResponse> for Oper<Register> {
let _inserted_community_follower = let _inserted_community_follower =
match CommunityFollower::follow(&conn, &community_follower_form) { match CommunityFollower::follow(&conn, &community_follower_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()), Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
}; };
// If its an admin, add them as a mod and follower to main // If its an admin, add them as a mod and follower to main
@ -331,15 +317,12 @@ impl Perform<LoginResponse> for Oper<Register> {
let _inserted_community_moderator = let _inserted_community_moderator =
match CommunityModerator::join(&conn, &community_moderator_form) { match CommunityModerator::join(&conn, &community_moderator_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => { Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
}
}; };
} }
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {
op: self.op.to_string(),
jwt: inserted_user.jwt(), jwt: inserted_user.jwt(),
}) })
} }
@ -351,7 +334,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -369,7 +352,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
Some(new_password_verify) => { Some(new_password_verify) => {
// Make sure passwords match // Make sure passwords match
if new_password != new_password_verify { if new_password != new_password_verify {
return Err(APIError::err(&self.op, "passwords_dont_match").into()); return Err(APIError::err("passwords_dont_match").into());
} }
// Check the old password // Check the old password
@ -378,14 +361,14 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
let valid: bool = let valid: bool =
verify(old_password, &read_user.password_encrypted).unwrap_or(false); verify(old_password, &read_user.password_encrypted).unwrap_or(false);
if !valid { if !valid {
return Err(APIError::err(&self.op, "password_incorrect").into()); return Err(APIError::err("password_incorrect").into());
} }
User_::update_password(&conn, user_id, &new_password)?.password_encrypted User_::update_password(&conn, user_id, &new_password)?.password_encrypted
} }
None => return Err(APIError::err(&self.op, "password_incorrect").into()), None => return Err(APIError::err("password_incorrect").into()),
} }
} }
None => return Err(APIError::err(&self.op, "passwords_dont_match").into()), None => return Err(APIError::err("passwords_dont_match").into()),
} }
} }
None => read_user.password_encrypted, None => read_user.password_encrypted,
@ -422,13 +405,12 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
"user_already_exists" "user_already_exists"
}; };
return Err(APIError::err(&self.op, err_type).into()); return Err(APIError::err(err_type).into());
} }
}; };
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {
op: self.op.to_string(),
jwt: updated_user.jwt(), jwt: updated_user.jwt(),
}) })
} }
@ -469,9 +451,7 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
.unwrap_or_else(|| "admin".to_string()), .unwrap_or_else(|| "admin".to_string()),
) { ) {
Ok(user) => user.id, Ok(user) => user.id,
Err(_e) => { Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
return Err(APIError::err(&self.op, "couldnt_find_that_username_or_email").into())
}
} }
} }
}; };
@ -514,7 +494,6 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
// Return the jwt // Return the jwt
Ok(GetUserDetailsResponse { Ok(GetUserDetailsResponse {
op: self.op.to_string(),
user: user_view, user: user_view,
follows, follows,
moderates, moderates,
@ -531,14 +510,14 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
// Make sure user is an admin // Make sure user is an admin
if !UserView::read(&conn, user_id)?.admin { if !UserView::read(&conn, user_id)?.admin {
return Err(APIError::err(&self.op, "not_an_admin").into()); return Err(APIError::err("not_an_admin").into());
} }
let read_user = User_::read(&conn, data.user_id)?; let read_user = User_::read(&conn, data.user_id)?;
@ -566,7 +545,7 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
match User_::update(&conn, data.user_id, &user_form) { match User_::update(&conn, data.user_id, &user_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user").into()), Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
}; };
// Mod tables // Mod tables
@ -584,10 +563,7 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
let creator_user = admins.remove(creator_index); let creator_user = admins.remove(creator_index);
admins.insert(0, creator_user); admins.insert(0, creator_user);
Ok(AddAdminResponse { Ok(AddAdminResponse { admins })
op: self.op.to_string(),
admins,
})
} }
} }
@ -597,14 +573,14 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
// Make sure user is an admin // Make sure user is an admin
if !UserView::read(&conn, user_id)?.admin { if !UserView::read(&conn, user_id)?.admin {
return Err(APIError::err(&self.op, "not_an_admin").into()); return Err(APIError::err("not_an_admin").into());
} }
let read_user = User_::read(&conn, data.user_id)?; let read_user = User_::read(&conn, data.user_id)?;
@ -632,7 +608,7 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
match User_::update(&conn, data.user_id, &user_form) { match User_::update(&conn, data.user_id, &user_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user").into()), Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
}; };
// Mod tables // Mod tables
@ -654,7 +630,6 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
let user_view = UserView::read(&conn, data.user_id)?; let user_view = UserView::read(&conn, data.user_id)?;
Ok(BanUserResponse { Ok(BanUserResponse {
op: self.op.to_string(),
user: user_view, user: user_view,
banned: data.ban, banned: data.ban,
}) })
@ -667,7 +642,7 @@ impl Perform<GetRepliesResponse> for Oper<GetReplies> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -681,10 +656,7 @@ impl Perform<GetRepliesResponse> for Oper<GetReplies> {
.limit(data.limit) .limit(data.limit)
.list()?; .list()?;
Ok(GetRepliesResponse { Ok(GetRepliesResponse { replies })
op: self.op.to_string(),
replies,
})
} }
} }
@ -694,7 +666,7 @@ impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -708,10 +680,7 @@ impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
.limit(data.limit) .limit(data.limit)
.list()?; .list()?;
Ok(GetUserMentionsResponse { Ok(GetUserMentionsResponse { mentions })
op: self.op.to_string(),
mentions,
})
} }
} }
@ -721,7 +690,7 @@ impl Perform<UserMentionResponse> for Oper<EditUserMention> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -737,13 +706,12 @@ impl Perform<UserMentionResponse> for Oper<EditUserMention> {
let _updated_user_mention = let _updated_user_mention =
match UserMention::update(&conn, user_mention.id, &user_mention_form) { match UserMention::update(&conn, user_mention.id, &user_mention_form) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
}; };
let user_mention_view = UserMentionView::read(&conn, user_mention.id, user_id)?; let user_mention_view = UserMentionView::read(&conn, user_mention.id, user_id)?;
Ok(UserMentionResponse { Ok(UserMentionResponse {
op: self.op.to_string(),
mention: user_mention_view, mention: user_mention_view,
}) })
} }
@ -755,7 +723,7 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -780,7 +748,7 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
let _updated_comment = match Comment::update(&conn, reply.id, &comment_form) { let _updated_comment = match Comment::update(&conn, reply.id, &comment_form) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
}; };
} }
@ -801,7 +769,7 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
let _updated_mention = let _updated_mention =
match UserMention::update(&conn, mention.user_mention_id, &mention_form) { match UserMention::update(&conn, mention.user_mention_id, &mention_form) {
Ok(mention) => mention, Ok(mention) => mention,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
}; };
} }
@ -825,14 +793,11 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
let _updated_message = match PrivateMessage::update(&conn, message.id, &private_message_form) let _updated_message = match PrivateMessage::update(&conn, message.id, &private_message_form)
{ {
Ok(message) => message, Ok(message) => message,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_private_message").into()), Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
}; };
} }
Ok(GetRepliesResponse { Ok(GetRepliesResponse { replies: vec![] })
op: self.op.to_string(),
replies: vec![],
})
} }
} }
@ -842,7 +807,7 @@ impl Perform<LoginResponse> for Oper<DeleteAccount> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -852,7 +817,7 @@ impl Perform<LoginResponse> for Oper<DeleteAccount> {
// Verify the password // Verify the password
let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false); let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
if !valid { if !valid {
return Err(APIError::err(&self.op, "password_incorrect").into()); return Err(APIError::err("password_incorrect").into());
} }
// Comments // Comments
@ -875,7 +840,7 @@ impl Perform<LoginResponse> for Oper<DeleteAccount> {
let _updated_comment = match Comment::update(&conn, comment.id, &comment_form) { let _updated_comment = match Comment::update(&conn, comment.id, &comment_form) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment").into()), Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
}; };
} }
@ -903,12 +868,11 @@ impl Perform<LoginResponse> for Oper<DeleteAccount> {
let _updated_post = match Post::update(&conn, post.id, &post_form) { let _updated_post = match Post::update(&conn, post.id, &post_form) {
Ok(post) => post, Ok(post) => post,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_post").into()), Err(_e) => return Err(APIError::err("couldnt_update_post").into()),
}; };
} }
Ok(LoginResponse { Ok(LoginResponse {
op: self.op.to_string(),
jwt: data.auth.to_owned(), jwt: data.auth.to_owned(),
}) })
} }
@ -921,7 +885,7 @@ impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
// Fetch that email // Fetch that email
let user: User_ = match User_::find_by_email(&conn, &data.email) { let user: User_ = match User_::find_by_email(&conn, &data.email) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_that_username_or_email").into()), Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
}; };
// Generate a random token // Generate a random token
@ -938,12 +902,10 @@ impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", user.name, hostname, &token); let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", user.name, hostname, &token);
match send_email(subject, user_email, &user.name, html) { match send_email(subject, user_email, &user.name, html) {
Ok(_o) => _o, Ok(_o) => _o,
Err(_e) => return Err(APIError::err(&self.op, &_e).into()), Err(_e) => return Err(APIError::err(&_e).into()),
}; };
Ok(PasswordResetResponse { Ok(PasswordResetResponse {})
op: self.op.to_string(),
})
} }
} }
@ -956,18 +918,17 @@ impl Perform<LoginResponse> for Oper<PasswordChange> {
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
return Err(APIError::err(&self.op, "passwords_dont_match").into()); return Err(APIError::err("passwords_dont_match").into());
} }
// Update the user with the new password // Update the user with the new password
let updated_user = match User_::update_password(&conn, user_id, &data.password) { let updated_user = match User_::update_password(&conn, user_id, &data.password) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user").into()), Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
}; };
// Return the jwt // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {
op: self.op.to_string(),
jwt: updated_user.jwt(), jwt: updated_user.jwt(),
}) })
} }
@ -979,7 +940,7 @@ impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -988,7 +949,7 @@ impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
let content_slurs_removed = remove_slurs(&data.content.to_owned()); let content_slurs_removed = remove_slurs(&data.content.to_owned());
@ -1005,7 +966,7 @@ impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
let inserted_private_message = match PrivateMessage::create(&conn, &private_message_form) { let inserted_private_message = match PrivateMessage::create(&conn, &private_message_form) {
Ok(private_message) => private_message, Ok(private_message) => private_message,
Err(_e) => { Err(_e) => {
return Err(APIError::err(&self.op, "couldnt_create_private_message").into()); return Err(APIError::err("couldnt_create_private_message").into());
} }
}; };
@ -1029,12 +990,9 @@ impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
} }
} }
let private_message_view = PrivateMessageView::read(&conn, inserted_private_message.id)?; let message = PrivateMessageView::read(&conn, inserted_private_message.id)?;
Ok(PrivateMessageResponse { Ok(PrivateMessageResponse { message })
op: self.op.to_string(),
message: private_message_view,
})
} }
} }
@ -1044,7 +1002,7 @@ impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -1053,14 +1011,14 @@ impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
// Check for a site ban // Check for a site ban
if UserView::read(&conn, user_id)?.banned { if UserView::read(&conn, user_id)?.banned {
return Err(APIError::err(&self.op, "site_ban").into()); return Err(APIError::err("site_ban").into());
} }
// Check to make sure they are the creator (or the recipient marking as read // 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) if !(data.read.is_some() && orig_private_message.recipient_id.eq(&user_id)
|| orig_private_message.creator_id.eq(&user_id)) || orig_private_message.creator_id.eq(&user_id))
{ {
return Err(APIError::err(&self.op, "no_private_message_edit_allowed").into()); return Err(APIError::err("no_private_message_edit_allowed").into());
} }
let content_slurs_removed = match &data.content { let content_slurs_removed = match &data.content {
@ -1084,15 +1042,12 @@ impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
let _updated_private_message = let _updated_private_message =
match PrivateMessage::update(&conn, data.edit_id, &private_message_form) { match PrivateMessage::update(&conn, data.edit_id, &private_message_form) {
Ok(private_message) => private_message, Ok(private_message) => private_message,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_private_message").into()), Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
}; };
let private_message_view = PrivateMessageView::read(&conn, data.edit_id)?; let message = PrivateMessageView::read(&conn, data.edit_id)?;
Ok(PrivateMessageResponse { Ok(PrivateMessageResponse { message })
op: self.op.to_string(),
message: private_message_view,
})
} }
} }
@ -1102,7 +1057,7 @@ impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
let claims = match Claims::decode(&data.auth) { let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims, Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()), Err(_e) => return Err(APIError::err("not_logged_in").into()),
}; };
let user_id = claims.id; let user_id = claims.id;
@ -1113,9 +1068,6 @@ impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
.unread_only(data.unread_only) .unread_only(data.unread_only)
.list()?; .list()?;
Ok(PrivateMessagesResponse { Ok(PrivateMessagesResponse { messages })
op: self.op.to_string(),
messages,
})
} }
} }

View file

@ -6,7 +6,7 @@ use actix::prelude::*;
use actix_web::*; use actix_web::*;
use diesel::r2d2::{ConnectionManager, Pool}; use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection; use diesel::PgConnection;
use lemmy_server::routes::{federation, feeds, index, nodeinfo, webfinger, websocket}; use lemmy_server::routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket};
use lemmy_server::settings::Settings; use lemmy_server::settings::Settings;
use lemmy_server::websocket::server::*; use lemmy_server::websocket::server::*;
use std::io; use std::io;
@ -44,6 +44,7 @@ async fn main() -> io::Result<()> {
.data(pool.clone()) .data(pool.clone())
.data(server.clone()) .data(server.clone())
// The routes // The routes
.configure(api::config)
.configure(federation::config) .configure(federation::config)
.configure(feeds::config) .configure(feeds::config)
.configure(index::config) .configure(index::config)

103
server/src/routes/api.rs Normal file
View file

@ -0,0 +1,103 @@
use crate::api::comment::*;
use crate::api::community::*;
use crate::api::post::*;
use crate::api::site::*;
use crate::api::user::*;
use crate::api::{Oper, Perform};
use actix_web::{web, HttpResponse};
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use failure::Error;
use serde::Serialize;
type DbParam = web::Data<Pool<ConnectionManager<PgConnection>>>;
#[rustfmt::skip]
pub fn config(cfg: &mut web::ServiceConfig) {
cfg
// Site
.route("/api/v1/site", web::get().to(route_get::<GetSite, GetSiteResponse>))
.route("/api/v1/categories", web::get().to(route_get::<ListCategories, ListCategoriesResponse>))
.route("/api/v1/modlog", web::get().to(route_get::<GetModlog, GetModlogResponse>))
.route("/api/v1/search", web::get().to(route_get::<Search, SearchResponse>))
// Community
.route("/api/v1/community", web::post().to(route_post::<CreateCommunity, CommunityResponse>))
.route("/api/v1/community", web::get().to(route_get::<GetCommunity, GetCommunityResponse>))
.route("/api/v1/community", web::put().to(route_post::<EditCommunity, CommunityResponse>))
.route("/api/v1/community/list", web::get().to(route_get::<ListCommunities, ListCommunitiesResponse>))
.route("/api/v1/community/follow", web::post().to(route_post::<FollowCommunity, CommunityResponse>))
// Post
.route("/api/v1/post", web::post().to(route_post::<CreatePost, PostResponse>))
.route("/api/v1/post", web::put().to(route_post::<EditPost, PostResponse>))
.route("/api/v1/post", web::get().to(route_get::<GetPost, GetPostResponse>))
.route("/api/v1/post/list", web::get().to(route_get::<GetPosts, GetPostsResponse>))
.route("/api/v1/post/like", web::post().to(route_post::<CreatePostLike, CreatePostLikeResponse>))
.route("/api/v1/post/save", web::put().to(route_post::<SavePost, PostResponse>))
// Comment
.route("/api/v1/comment", web::post().to(route_post::<CreateComment, CommentResponse>))
.route("/api/v1/comment", web::put().to(route_post::<EditComment, CommentResponse>))
.route("/api/v1/comment/like", web::post().to(route_post::<CreateCommentLike, CommentResponse>))
.route("/api/v1/comment/save", web::put().to(route_post::<SaveComment, CommentResponse>))
// User
.route("/api/v1/user", web::get().to(route_get::<GetUserDetails, GetUserDetailsResponse>))
.route("/api/v1/user/mention", web::get().to(route_get::<GetUserMentions, GetUserMentionsResponse>))
.route("/api/v1/user/mention", web::put().to(route_post::<EditUserMention, UserMentionResponse>))
.route("/api/v1/user/replies", web::get().to(route_get::<GetReplies, GetRepliesResponse>))
.route("/api/v1/user/followed_communities", web::get().to(route_get::<GetFollowedCommunities, GetFollowedCommunitiesResponse>))
// Mod actions
.route("/api/v1/community/transfer", web::post().to(route_post::<TransferCommunity, GetCommunityResponse>))
.route("/api/v1/community/ban_user", web::post().to(route_post::<BanFromCommunity, BanFromCommunityResponse>))
.route("/api/v1/community/mod", web::post().to(route_post::<AddModToCommunity, AddModToCommunityResponse>))
// Admin actions
.route("/api/v1/site", web::post().to(route_post::<CreateSite, SiteResponse>))
.route("/api/v1/site", web::put().to(route_post::<EditSite, SiteResponse>))
.route("/api/v1/site/transfer", web::post().to(route_post::<TransferSite, GetSiteResponse>))
.route("/api/v1/admin/add", web::post().to(route_post::<AddAdmin, AddAdminResponse>))
.route("/api/v1/user/ban", web::post().to(route_post::<BanUser, BanUserResponse>))
// User account actions
.route("/api/v1/user/login", web::post().to(route_post::<Login, LoginResponse>))
.route("/api/v1/user/register", web::post().to(route_post::<Register, LoginResponse>))
.route("/api/v1/user/delete_account", web::post().to(route_post::<DeleteAccount, LoginResponse>))
.route("/api/v1/user/password_reset", web::post().to(route_post::<PasswordReset, PasswordResetResponse>))
.route("/api/v1/user/password_change", web::post().to(route_post::<PasswordChange, LoginResponse>))
.route("/api/v1/user/mark_all_as_read", web::post().to(route_post::<MarkAllAsRead, GetRepliesResponse>))
.route("/api/v1/user/save_user_settings", web::put().to(route_post::<SaveUserSettings, LoginResponse>));
}
fn perform<Request, Response>(data: Request, db: DbParam) -> Result<HttpResponse, Error>
where
Response: Serialize,
Oper<Request>: Perform<Response>,
{
let conn = match db.get() {
Ok(c) => c,
Err(e) => return Err(format_err!("{}", e)),
};
let oper: Oper<Request> = Oper::new(data);
let response = oper.perform(&conn);
Ok(HttpResponse::Ok().json(response?))
}
async fn route_get<Data, Response>(
data: web::Query<Data>,
db: DbParam,
) -> Result<HttpResponse, Error>
where
Data: Serialize,
Response: Serialize,
Oper<Data>: Perform<Response>,
{
perform::<Data, Response>(data.0, db)
}
async fn route_post<Data, Response>(
data: web::Json<Data>,
db: DbParam,
) -> Result<HttpResponse, Error>
where
Data: Serialize,
Response: Serialize,
Oper<Data>: Perform<Response>,
{
perform::<Data, Response>(data.0, db)
}

View file

@ -1,3 +1,4 @@
pub mod api;
pub mod federation; pub mod federation;
pub mod feeds; pub mod feeds;
pub mod index; pub mod index;

View file

@ -1 +1,47 @@
pub mod server; pub mod server;
#[derive(EnumString, ToString, Debug)]
pub enum UserOperation {
Login,
Register,
CreateCommunity,
CreatePost,
ListCommunities,
ListCategories,
GetPost,
GetCommunity,
CreateComment,
EditComment,
SaveComment,
CreateCommentLike,
GetPosts,
CreatePostLike,
EditPost,
SavePost,
EditCommunity,
FollowCommunity,
GetFollowedCommunities,
GetUserDetails,
GetReplies,
GetUserMentions,
EditUserMention,
GetModlog,
BanFromCommunity,
AddModToCommunity,
CreateSite,
EditSite,
GetSite,
AddAdmin,
BanUser,
Search,
MarkAllAsRead,
SaveUserSettings,
TransferCommunity,
TransferSite,
DeleteAccount,
PasswordReset,
PasswordChange,
CreatePrivateMessage,
EditPrivateMessage,
GetPrivateMessages,
}

View file

@ -3,7 +3,7 @@
//! room through `ChatServer`. //! room through `ChatServer`.
use actix::prelude::*; use actix::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool}; use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
use diesel::PgConnection; use diesel::PgConnection;
use failure::Error; use failure::Error;
use rand::{rngs::ThreadRng, Rng}; use rand::{rngs::ThreadRng, Rng};
@ -19,6 +19,7 @@ use crate::api::post::*;
use crate::api::site::*; use crate::api::site::*;
use crate::api::user::*; use crate::api::user::*;
use crate::api::*; use crate::api::*;
use crate::websocket::UserOperation;
use crate::Settings; use crate::Settings;
/// Chat server sends this messages to session /// Chat server sends this messages to session
@ -201,7 +202,6 @@ impl ChatServer {
); );
Err( Err(
APIError { APIError {
op: "Rate Limit".to_string(),
message: format!("Too many requests. {} per {} seconds", rate, per), message: format!("Too many requests. {} per {} seconds", rate, per),
} }
.into(), .into(),
@ -295,11 +295,42 @@ impl Handler<StandardMessage> for ChatServer {
} }
} }
#[derive(Serialize)]
struct WebsocketResponse<T> {
op: String,
data: T,
}
fn to_json_string<T>(op: &UserOperation, data: T) -> Result<String, Error>
where
T: Serialize,
{
let response = WebsocketResponse {
op: op.to_string(),
data,
};
Ok(serde_json::to_string(&response)?)
}
fn do_user_operation<'a, Data, Response>(
op: UserOperation,
data: &str,
conn: &PooledConnection<ConnectionManager<PgConnection>>,
) -> Result<String, Error>
where
for<'de> Data: Deserialize<'de> + 'a,
Response: Serialize,
Oper<Data>: Perform<Response>,
{
let parsed_data: Data = serde_json::from_str(data)?;
let res = Oper::new(parsed_data).perform(&conn)?;
to_json_string(&op, &res)
}
fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<String, Error> { fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<String, Error> {
let json: Value = serde_json::from_str(&msg.msg)?; let json: Value = serde_json::from_str(&msg.msg)?;
let data = &json["data"].to_string(); let data = &json["data"].to_string();
let op = &json["op"].as_str().ok_or(APIError { let op = &json["op"].as_str().ok_or(APIError {
op: "Unknown op type".to_string(),
message: "Unknown op type".to_string(), message: "Unknown op type".to_string(),
})?; })?;
@ -307,261 +338,194 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
let user_operation: UserOperation = UserOperation::from_str(&op)?; let user_operation: UserOperation = UserOperation::from_str(&op)?;
// TODO: none of the chat messages are going to work if stuff is submitted via http api,
// need to move that handling elsewhere
match user_operation { match user_operation {
UserOperation::Login => { UserOperation::Login => do_user_operation::<Login, LoginResponse>(user_operation, data, &conn),
let login: Login = serde_json::from_str(data)?;
let res = Oper::new(user_operation, login).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
}
UserOperation::Register => { UserOperation::Register => {
let register: Register = serde_json::from_str(data)?;
let res = Oper::new(user_operation, register).perform(&conn);
if res.is_ok() {
chat.check_rate_limit_register(msg.id)?; chat.check_rate_limit_register(msg.id)?;
} do_user_operation::<Register, LoginResponse>(user_operation, data, &conn)
Ok(serde_json::to_string(&res?)?)
} }
UserOperation::GetUserDetails => { UserOperation::GetUserDetails => {
let get_user_details: GetUserDetails = serde_json::from_str(data)?; do_user_operation::<GetUserDetails, GetUserDetailsResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, get_user_details).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::SaveUserSettings => { UserOperation::SaveUserSettings => {
let save_user_settings: SaveUserSettings = serde_json::from_str(data)?; do_user_operation::<SaveUserSettings, LoginResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, save_user_settings).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::AddAdmin => { UserOperation::AddAdmin => {
let add_admin: AddAdmin = serde_json::from_str(data)?; do_user_operation::<AddAdmin, AddAdminResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, add_admin).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::BanUser => { UserOperation::BanUser => {
let ban_user: BanUser = serde_json::from_str(data)?; do_user_operation::<BanUser, BanUserResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, ban_user).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::GetReplies => { UserOperation::GetReplies => {
let get_replies: GetReplies = serde_json::from_str(data)?; do_user_operation::<GetReplies, GetRepliesResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, get_replies).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::GetUserMentions => { UserOperation::GetUserMentions => {
let get_user_mentions: GetUserMentions = serde_json::from_str(data)?; do_user_operation::<GetUserMentions, GetUserMentionsResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, get_user_mentions).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::EditUserMention => { UserOperation::EditUserMention => {
let edit_user_mention: EditUserMention = serde_json::from_str(data)?; do_user_operation::<EditUserMention, UserMentionResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, edit_user_mention).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::MarkAllAsRead => { UserOperation::MarkAllAsRead => {
let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?; do_user_operation::<MarkAllAsRead, GetRepliesResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, mark_all_as_read).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::GetCommunity => { UserOperation::GetCommunity => {
let get_community: GetCommunity = serde_json::from_str(data)?; do_user_operation::<GetCommunity, GetCommunityResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, get_community).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::ListCommunities => { UserOperation::ListCommunities => {
let list_communities: ListCommunities = serde_json::from_str(data)?; do_user_operation::<ListCommunities, ListCommunitiesResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, list_communities).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::CreateCommunity => { UserOperation::CreateCommunity => {
chat.check_rate_limit_register(msg.id)?; chat.check_rate_limit_register(msg.id)?;
let create_community: CreateCommunity = serde_json::from_str(data)?; do_user_operation::<CreateCommunity, CommunityResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, create_community).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::EditCommunity => { UserOperation::EditCommunity => {
let edit_community: EditCommunity = serde_json::from_str(data)?; let edit_community: EditCommunity = serde_json::from_str(data)?;
let res = Oper::new(user_operation, edit_community).perform(&conn)?; let res = Oper::new(edit_community).perform(&conn)?;
let mut community_sent: CommunityResponse = res.clone(); let mut community_sent: CommunityResponse = res.clone();
community_sent.community.user_id = None; community_sent.community.user_id = None;
community_sent.community.subscribed = None; community_sent.community.subscribed = None;
let community_sent_str = serde_json::to_string(&community_sent)?; let community_sent_str = to_json_string(&user_operation, &community_sent)?;
chat.send_community_message(community_sent.community.id, &community_sent_str, msg.id)?; chat.send_community_message(community_sent.community.id, &community_sent_str, msg.id)?;
Ok(serde_json::to_string(&res)?) to_json_string(&user_operation, &res)
} }
UserOperation::FollowCommunity => { UserOperation::FollowCommunity => {
let follow_community: FollowCommunity = serde_json::from_str(data)?; do_user_operation::<FollowCommunity, CommunityResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, follow_community).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
}
UserOperation::GetFollowedCommunities => {
let followed_communities: GetFollowedCommunities = serde_json::from_str(data)?;
let res = Oper::new(user_operation, followed_communities).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::GetFollowedCommunities => do_user_operation::<
GetFollowedCommunities,
GetFollowedCommunitiesResponse,
>(user_operation, data, &conn),
UserOperation::BanFromCommunity => { UserOperation::BanFromCommunity => {
let ban_from_community: BanFromCommunity = serde_json::from_str(data)?; let ban_from_community: BanFromCommunity = serde_json::from_str(data)?;
let community_id = ban_from_community.community_id; let community_id = ban_from_community.community_id;
let res = Oper::new(user_operation, ban_from_community).perform(&conn)?; let res = Oper::new(ban_from_community).perform(&conn)?;
let res_str = serde_json::to_string(&res)?; let res_str = to_json_string(&user_operation, &res)?;
chat.send_community_message(community_id, &res_str, msg.id)?; chat.send_community_message(community_id, &res_str, msg.id)?;
Ok(res_str) Ok(res_str)
} }
UserOperation::AddModToCommunity => { UserOperation::AddModToCommunity => {
let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?; let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?;
let community_id = mod_add_to_community.community_id; let community_id = mod_add_to_community.community_id;
let res = Oper::new(user_operation, mod_add_to_community).perform(&conn)?; let res = Oper::new(mod_add_to_community).perform(&conn)?;
let res_str = serde_json::to_string(&res)?; let res_str = to_json_string(&user_operation, &res)?;
chat.send_community_message(community_id, &res_str, msg.id)?; chat.send_community_message(community_id, &res_str, msg.id)?;
Ok(res_str) Ok(res_str)
} }
UserOperation::ListCategories => { UserOperation::ListCategories => {
let list_categories: ListCategories = ListCategories; do_user_operation::<ListCategories, ListCategoriesResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, list_categories).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::CreatePost => { UserOperation::CreatePost => {
chat.check_rate_limit_post(msg.id)?; chat.check_rate_limit_post(msg.id)?;
let create_post: CreatePost = serde_json::from_str(data)?; do_user_operation::<CreatePost, PostResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, create_post).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::GetPost => { UserOperation::GetPost => {
let get_post: GetPost = serde_json::from_str(data)?; let get_post: GetPost = serde_json::from_str(data)?;
chat.join_room(get_post.id, msg.id); chat.join_room(get_post.id, msg.id);
let res = Oper::new(user_operation, get_post).perform(&conn)?; let res = Oper::new(get_post).perform(&conn)?;
Ok(serde_json::to_string(&res)?) to_json_string(&user_operation, &res)
} }
UserOperation::GetPosts => { UserOperation::GetPosts => {
let get_posts: GetPosts = serde_json::from_str(data)?; do_user_operation::<GetPosts, GetPostsResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, get_posts).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::CreatePostLike => { UserOperation::CreatePostLike => {
chat.check_rate_limit_message(msg.id)?; chat.check_rate_limit_message(msg.id)?;
let create_post_like: CreatePostLike = serde_json::from_str(data)?; do_user_operation::<CreatePostLike, CreatePostLikeResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, create_post_like).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::EditPost => { UserOperation::EditPost => {
let edit_post: EditPost = serde_json::from_str(data)?; let edit_post: EditPost = serde_json::from_str(data)?;
let res = Oper::new(user_operation, edit_post).perform(&conn)?; let res = Oper::new(edit_post).perform(&conn)?;
let mut post_sent = res.clone(); let mut post_sent = res.clone();
post_sent.post.my_vote = None; post_sent.post.my_vote = None;
let post_sent_str = serde_json::to_string(&post_sent)?; let post_sent_str = to_json_string(&user_operation, &post_sent)?;
chat.send_room_message(post_sent.post.id, &post_sent_str, msg.id); chat.send_room_message(post_sent.post.id, &post_sent_str, msg.id);
Ok(serde_json::to_string(&res)?) to_json_string(&user_operation, &res)
} }
UserOperation::SavePost => { UserOperation::SavePost => {
let save_post: SavePost = serde_json::from_str(data)?; do_user_operation::<SavePost, PostResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, save_post).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::CreateComment => { UserOperation::CreateComment => {
chat.check_rate_limit_message(msg.id)?; chat.check_rate_limit_message(msg.id)?;
let create_comment: CreateComment = serde_json::from_str(data)?; let create_comment: CreateComment = serde_json::from_str(data)?;
let post_id = create_comment.post_id; let post_id = create_comment.post_id;
let res = Oper::new(user_operation, create_comment).perform(&conn)?; let res = Oper::new(create_comment).perform(&conn)?;
let mut comment_sent = res.clone(); let mut comment_sent = res.clone();
comment_sent.comment.my_vote = None; comment_sent.comment.my_vote = None;
comment_sent.comment.user_id = None; comment_sent.comment.user_id = None;
let comment_sent_str = serde_json::to_string(&comment_sent)?; let comment_sent_str = to_json_string(&user_operation, &comment_sent)?;
chat.send_room_message(post_id, &comment_sent_str, msg.id); chat.send_room_message(post_id, &comment_sent_str, msg.id);
Ok(serde_json::to_string(&res)?) to_json_string(&user_operation, &res)
} }
UserOperation::EditComment => { UserOperation::EditComment => {
let edit_comment: EditComment = serde_json::from_str(data)?; let edit_comment: EditComment = serde_json::from_str(data)?;
let post_id = edit_comment.post_id; let post_id = edit_comment.post_id;
let res = Oper::new(user_operation, edit_comment).perform(&conn)?; let res = Oper::new(edit_comment).perform(&conn)?;
let mut comment_sent = res.clone(); let mut comment_sent = res.clone();
comment_sent.comment.my_vote = None; comment_sent.comment.my_vote = None;
comment_sent.comment.user_id = None; comment_sent.comment.user_id = None;
let comment_sent_str = serde_json::to_string(&comment_sent)?; let comment_sent_str = to_json_string(&user_operation, &comment_sent)?;
chat.send_room_message(post_id, &comment_sent_str, msg.id); chat.send_room_message(post_id, &comment_sent_str, msg.id);
Ok(serde_json::to_string(&res)?) to_json_string(&user_operation, &res)
} }
UserOperation::SaveComment => { UserOperation::SaveComment => {
let save_comment: SaveComment = serde_json::from_str(data)?; do_user_operation::<SaveComment, CommentResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, save_comment).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::CreateCommentLike => { UserOperation::CreateCommentLike => {
chat.check_rate_limit_message(msg.id)?; chat.check_rate_limit_message(msg.id)?;
let create_comment_like: CreateCommentLike = serde_json::from_str(data)?; let create_comment_like: CreateCommentLike = serde_json::from_str(data)?;
let post_id = create_comment_like.post_id; let post_id = create_comment_like.post_id;
let res = Oper::new(user_operation, create_comment_like).perform(&conn)?; let res = Oper::new(create_comment_like).perform(&conn)?;
let mut comment_sent = res.clone(); let mut comment_sent = res.clone();
comment_sent.comment.my_vote = None; comment_sent.comment.my_vote = None;
comment_sent.comment.user_id = None; comment_sent.comment.user_id = None;
let comment_sent_str = serde_json::to_string(&comment_sent)?; let comment_sent_str = to_json_string(&user_operation, &comment_sent)?;
chat.send_room_message(post_id, &comment_sent_str, msg.id); chat.send_room_message(post_id, &comment_sent_str, msg.id);
Ok(serde_json::to_string(&res)?) to_json_string(&user_operation, &res)
} }
UserOperation::GetModlog => { UserOperation::GetModlog => {
let get_modlog: GetModlog = serde_json::from_str(data)?; do_user_operation::<GetModlog, GetModlogResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, get_modlog).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::CreateSite => { UserOperation::CreateSite => {
let create_site: CreateSite = serde_json::from_str(data)?; do_user_operation::<CreateSite, SiteResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, create_site).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::EditSite => { UserOperation::EditSite => {
let edit_site: EditSite = serde_json::from_str(data)?; do_user_operation::<EditSite, SiteResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, edit_site).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::GetSite => { UserOperation::GetSite => {
let online: usize = chat.sessions.len(); let online: usize = chat.sessions.len();
let get_site: GetSite = serde_json::from_str(data)?; let get_site: GetSite = serde_json::from_str(data)?;
let mut res = Oper::new(user_operation, get_site).perform(&conn)?; let mut res = Oper::new(get_site).perform(&conn)?;
res.online = online; res.online = online;
Ok(serde_json::to_string(&res)?) to_json_string(&user_operation, &res)
} }
UserOperation::Search => { UserOperation::Search => {
let search: Search = serde_json::from_str(data)?; do_user_operation::<Search, SearchResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, search).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::TransferCommunity => { UserOperation::TransferCommunity => {
let transfer_community: TransferCommunity = serde_json::from_str(data)?; do_user_operation::<TransferCommunity, GetCommunityResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, transfer_community).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::TransferSite => { UserOperation::TransferSite => {
let transfer_site: TransferSite = serde_json::from_str(data)?; do_user_operation::<TransferSite, GetSiteResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, transfer_site).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::DeleteAccount => { UserOperation::DeleteAccount => {
let delete_account: DeleteAccount = serde_json::from_str(data)?; do_user_operation::<DeleteAccount, LoginResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, delete_account).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::PasswordReset => { UserOperation::PasswordReset => {
let password_reset: PasswordReset = serde_json::from_str(data)?; do_user_operation::<PasswordReset, PasswordResetResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, password_reset).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::PasswordChange => { UserOperation::PasswordChange => {
let password_change: PasswordChange = serde_json::from_str(data)?; do_user_operation::<PasswordChange, LoginResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, password_change).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::CreatePrivateMessage => { UserOperation::CreatePrivateMessage => {
chat.check_rate_limit_message(msg.id)?; chat.check_rate_limit_message(msg.id)?;
let create_private_message: CreatePrivateMessage = serde_json::from_str(data)?; do_user_operation::<CreatePrivateMessage, PrivateMessageResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, create_private_message).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::EditPrivateMessage => { UserOperation::EditPrivateMessage => {
let edit_private_message: EditPrivateMessage = serde_json::from_str(data)?; do_user_operation::<EditPrivateMessage, PrivateMessageResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, edit_private_message).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
UserOperation::GetPrivateMessages => { UserOperation::GetPrivateMessages => {
let messages: GetPrivateMessages = serde_json::from_str(data)?; do_user_operation::<GetPrivateMessages, PrivateMessagesResponse>(user_operation, data, &conn)
let res = Oper::new(user_operation, messages).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
} }
} }
} }

View file

@ -10,9 +10,9 @@ import {
} from '../interfaces'; } from '../interfaces';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { import {
wsJsonToRes,
capitalizeFirstLetter, capitalizeFirstLetter,
mentionDropdownFetchLimit, mentionDropdownFetchLimit,
msgOp,
mdToHtml, mdToHtml,
randomStr, randomStr,
markdownHelpUrl, markdownHelpUrl,
@ -312,10 +312,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
this.userSub = WebSocketService.Instance.subject.subscribe( this.userSub = WebSocketService.Instance.subject.subscribe(
msg => { msg => {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (op == UserOperation.Search) { if (res.op == UserOperation.Search) {
let res: SearchResponse = msg; let data = res.data as SearchResponse;
let users = res.users.map(u => { let users = data.users.map(u => {
return { key: u.name }; return { key: u.name };
}); });
cb(users); cb(users);
@ -344,10 +344,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
this.communitySub = WebSocketService.Instance.subject.subscribe( this.communitySub = WebSocketService.Instance.subject.subscribe(
msg => { msg => {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (op == UserOperation.Search) { if (res.op == UserOperation.Search) {
let res: SearchResponse = msg; let data = res.data as SearchResponse;
let communities = res.communities.map(u => { let communities = data.communities.map(u => {
return { key: u.name }; return { key: u.name };
}); });
cb(communities); cb(communities);

View file

@ -10,9 +10,10 @@ import {
FollowCommunityForm, FollowCommunityForm,
ListCommunitiesForm, ListCommunitiesForm,
SortType, SortType,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp, toast } from '../utils'; import { wsJsonToRes, toast } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -231,15 +232,15 @@ export class Communities extends Component<any, CommunitiesState> {
WebSocketService.Instance.listCommunities(listCommunitiesForm); WebSocketService.Instance.listCommunities(listCommunitiesForm);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.ListCommunities) { } else if (res.op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg; let data = res.data as ListCommunitiesResponse;
this.state.communities = res.communities; this.state.communities = data.communities;
this.state.communities.sort( this.state.communities.sort(
(a, b) => b.number_of_subscribers - a.number_of_subscribers (a, b) => b.number_of_subscribers - a.number_of_subscribers
); );
@ -248,11 +249,11 @@ export class Communities extends Component<any, CommunitiesState> {
this.setState(this.state); this.setState(this.state);
let table = document.querySelector('#community_table'); let table = document.querySelector('#community_table');
Sortable.initTable(table); Sortable.initTable(table);
} else if (op == UserOperation.FollowCommunity) { } else if (res.op == UserOperation.FollowCommunity) {
let res: CommunityResponse = msg; let data = res.data as CommunityResponse;
let found = this.state.communities.find(c => c.id == res.community.id); let found = this.state.communities.find(c => c.id == data.community.id);
found.subscribed = res.community.subscribed; found.subscribed = data.community.subscribed;
found.number_of_subscribers = res.community.number_of_subscribers; found.number_of_subscribers = data.community.number_of_subscribers;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -8,9 +8,10 @@ import {
ListCategoriesResponse, ListCategoriesResponse,
CommunityResponse, CommunityResponse,
GetSiteResponse, GetSiteResponse,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp, capitalizeFirstLetter, toast } from '../utils'; import { wsJsonToRes, capitalizeFirstLetter, toast } from '../utils';
import autosize from 'autosize'; import autosize from 'autosize';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -239,34 +240,34 @@ export class CommunityForm extends Component<
i.props.onCancel(); i.props.onCancel();
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
console.log(msg); console.log(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;
} else if (op == UserOperation.ListCategories) { } else if (res.op == UserOperation.ListCategories) {
let res: ListCategoriesResponse = msg; let data = res.data as ListCategoriesResponse;
this.state.categories = res.categories; this.state.categories = data.categories;
if (!this.props.community) { if (!this.props.community) {
this.state.communityForm.category_id = res.categories[0].id; this.state.communityForm.category_id = data.categories[0].id;
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateCommunity) { } else if (res.op == UserOperation.CreateCommunity) {
let res: CommunityResponse = msg; let data = res.data as CommunityResponse;
this.state.loading = false; this.state.loading = false;
this.props.onCreate(res.community); this.props.onCreate(data.community);
} }
// TODO is ths necessary // TODO is this necessary
else if (op == UserOperation.EditCommunity) { else if (res.op == UserOperation.EditCommunity) {
let res: CommunityResponse = msg; let data = res.data as CommunityResponse;
this.state.loading = false; this.state.loading = false;
this.props.onEdit(res.community); this.props.onEdit(data.community);
} else if (op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let res: GetSiteResponse = msg; let data = res.data as GetSiteResponse;
this.state.enable_nsfw = res.site.enable_nsfw; this.state.enable_nsfw = data.site.enable_nsfw;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -14,13 +14,14 @@ import {
ListingType, ListingType,
GetPostsResponse, GetPostsResponse,
CreatePostLikeResponse, CreatePostLikeResponse,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { PostListings } from './post-listings'; import { PostListings } from './post-listings';
import { SortSelect } from './sort-select'; import { SortSelect } from './sort-select';
import { Sidebar } from './sidebar'; import { Sidebar } from './sidebar';
import { import {
msgOp, wsJsonToRes,
routeSortTypeToEnum, routeSortTypeToEnum,
fetchLimit, fetchLimit,
postRefetchSeconds, postRefetchSeconds,
@ -254,43 +255,43 @@ export class Community extends Component<any, State> {
WebSocketService.Instance.getPosts(getPostsForm); WebSocketService.Instance.getPosts(getPostsForm);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.context.router.history.push('/'); this.context.router.history.push('/');
return; return;
} else if (op == UserOperation.GetCommunity) { } else if (res.op == UserOperation.GetCommunity) {
let res: GetCommunityResponse = msg; let data = res.data as GetCommunityResponse;
this.state.community = res.community; this.state.community = data.community;
this.state.moderators = res.moderators; this.state.moderators = data.moderators;
this.state.admins = res.admins; this.state.admins = data.admins;
document.title = `/c/${this.state.community.name} - ${WebSocketService.Instance.site.name}`; document.title = `/c/${this.state.community.name} - ${WebSocketService.Instance.site.name}`;
this.setState(this.state); this.setState(this.state);
this.keepFetchingPosts(); this.keepFetchingPosts();
} else if (op == UserOperation.EditCommunity) { } else if (res.op == UserOperation.EditCommunity) {
let res: CommunityResponse = msg; let data = res.data as CommunityResponse;
this.state.community = res.community; this.state.community = data.community;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.FollowCommunity) { } else if (res.op == UserOperation.FollowCommunity) {
let res: CommunityResponse = msg; let data = res.data as CommunityResponse;
this.state.community.subscribed = res.community.subscribed; this.state.community.subscribed = data.community.subscribed;
this.state.community.number_of_subscribers = this.state.community.number_of_subscribers =
res.community.number_of_subscribers; data.community.number_of_subscribers;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.GetPosts) { } else if (res.op == UserOperation.GetPosts) {
let res: GetPostsResponse = msg; let data = res.data as GetPostsResponse;
this.state.posts = res.posts; this.state.posts = data.posts;
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) { } else if (res.op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg; let data = res.data as CreatePostLikeResponse;
let found = this.state.posts.find(c => c.id == res.post.id); let found = this.state.posts.find(c => c.id == data.post.id);
found.my_vote = res.post.my_vote; found.my_vote = data.post.my_vote;
found.score = res.post.score; found.score = data.post.score;
found.upvotes = res.post.upvotes; found.upvotes = data.post.upvotes;
found.downvotes = res.post.downvotes; found.downvotes = data.post.downvotes;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -12,13 +12,14 @@ import {
GetUserMentionsResponse, GetUserMentionsResponse,
UserMentionResponse, UserMentionResponse,
CommentResponse, CommentResponse,
WebSocketJsonResponse,
PrivateMessage as PrivateMessageI, PrivateMessage as PrivateMessageI,
GetPrivateMessagesForm, GetPrivateMessagesForm,
PrivateMessagesResponse, PrivateMessagesResponse,
PrivateMessageResponse, PrivateMessageResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, fetchLimit, isCommentType, toast } from '../utils'; import { wsJsonToRes, fetchLimit, isCommentType, toast } from '../utils';
import { CommentNodes } from './comment-nodes'; import { CommentNodes } from './comment-nodes';
import { PrivateMessage } from './private-message'; import { PrivateMessage } from './private-message';
import { SortSelect } from './sort-select'; import { SortSelect } from './sort-select';
@ -320,122 +321,122 @@ export class Inbox extends Component<any, InboxState> {
WebSocketService.Instance.markAllAsRead(); WebSocketService.Instance.markAllAsRead();
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetReplies) { } else if (res.op == UserOperation.GetReplies) {
let res: GetRepliesResponse = msg; let data = res.data as GetRepliesResponse;
this.state.replies = res.replies; this.state.replies = data.replies;
this.sendUnreadCount(); this.sendUnreadCount();
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.GetUserMentions) { } else if (res.op == UserOperation.GetUserMentions) {
let res: GetUserMentionsResponse = msg; let data = res.data as GetUserMentionsResponse;
this.state.mentions = res.mentions; this.state.mentions = data.mentions;
this.sendUnreadCount(); this.sendUnreadCount();
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.GetPrivateMessages) { } else if (res.op == UserOperation.GetPrivateMessages) {
let res: PrivateMessagesResponse = msg; let data = res.data as PrivateMessagesResponse;
this.state.messages = res.messages; this.state.messages = data.messages;
this.sendUnreadCount(); this.sendUnreadCount();
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.EditPrivateMessage) { } else if (res.op == UserOperation.EditPrivateMessage) {
let res: PrivateMessageResponse = msg; let data = res.data as PrivateMessageResponse;
let found: PrivateMessageI = this.state.messages.find( let found: PrivateMessageI = this.state.messages.find(
m => m.id === res.message.id m => m.id === data.message.id
); );
found.content = res.message.content; found.content = data.message.content;
found.updated = res.message.updated; found.updated = data.message.updated;
found.deleted = res.message.deleted; found.deleted = data.message.deleted;
// If youre in the unread view, just remove it from the list // If youre in the unread view, just remove it from the list
if (this.state.unreadOrAll == UnreadOrAll.Unread && res.message.read) { if (this.state.unreadOrAll == UnreadOrAll.Unread && data.message.read) {
this.state.messages = this.state.messages.filter( this.state.messages = this.state.messages.filter(
r => r.id !== res.message.id r => r.id !== data.message.id
); );
} else { } else {
let found = this.state.messages.find(c => c.id == res.message.id); let found = this.state.messages.find(c => c.id == data.message.id);
found.read = res.message.read; found.read = data.message.read;
} }
this.sendUnreadCount(); this.sendUnreadCount();
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.MarkAllAsRead) { } else if (res.op == UserOperation.MarkAllAsRead) {
this.state.replies = []; this.state.replies = [];
this.state.mentions = []; this.state.mentions = [];
this.state.messages = []; this.state.messages = [];
this.sendUnreadCount(); this.sendUnreadCount();
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.EditComment) { } else if (res.op == UserOperation.EditComment) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found = this.state.replies.find(c => c.id == res.comment.id); let found = this.state.replies.find(c => c.id == data.comment.id);
found.content = res.comment.content; found.content = data.comment.content;
found.updated = res.comment.updated; found.updated = data.comment.updated;
found.removed = res.comment.removed; found.removed = data.comment.removed;
found.deleted = res.comment.deleted; found.deleted = data.comment.deleted;
found.upvotes = res.comment.upvotes; found.upvotes = data.comment.upvotes;
found.downvotes = res.comment.downvotes; found.downvotes = data.comment.downvotes;
found.score = res.comment.score; found.score = data.comment.score;
// If youre in the unread view, just remove it from the list // If youre in the unread view, just remove it from the list
if (this.state.unreadOrAll == UnreadOrAll.Unread && res.comment.read) { if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) {
this.state.replies = this.state.replies.filter( this.state.replies = this.state.replies.filter(
r => r.id !== res.comment.id r => r.id !== data.comment.id
); );
} else { } else {
let found = this.state.replies.find(c => c.id == res.comment.id); let found = this.state.replies.find(c => c.id == data.comment.id);
found.read = res.comment.read; found.read = data.comment.read;
} }
this.sendUnreadCount(); this.sendUnreadCount();
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.EditUserMention) { } else if (res.op == UserOperation.EditUserMention) {
let res: UserMentionResponse = msg; let data = res.data as UserMentionResponse;
let found = this.state.mentions.find(c => c.id == res.mention.id); let found = this.state.mentions.find(c => c.id == data.mention.id);
found.content = res.mention.content; found.content = data.mention.content;
found.updated = res.mention.updated; found.updated = data.mention.updated;
found.removed = res.mention.removed; found.removed = data.mention.removed;
found.deleted = res.mention.deleted; found.deleted = data.mention.deleted;
found.upvotes = res.mention.upvotes; found.upvotes = data.mention.upvotes;
found.downvotes = res.mention.downvotes; found.downvotes = data.mention.downvotes;
found.score = res.mention.score; found.score = data.mention.score;
// If youre in the unread view, just remove it from the list // If youre in the unread view, just remove it from the list
if (this.state.unreadOrAll == UnreadOrAll.Unread && res.mention.read) { if (this.state.unreadOrAll == UnreadOrAll.Unread && data.mention.read) {
this.state.mentions = this.state.mentions.filter( this.state.mentions = this.state.mentions.filter(
r => r.id !== res.mention.id r => r.id !== data.mention.id
); );
} else { } else {
let found = this.state.mentions.find(c => c.id == res.mention.id); let found = this.state.mentions.find(c => c.id == data.mention.id);
found.read = res.mention.read; found.read = data.mention.read;
} }
this.sendUnreadCount(); this.sendUnreadCount();
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateComment) { } else if (res.op == UserOperation.CreateComment) {
// let res: CommentResponse = msg; // let res: CommentResponse = msg;
toast(i18n.t('reply_sent')); toast(i18n.t('reply_sent'));
// this.state.replies.unshift(res.comment); // TODO do this right // this.state.replies.unshift(res.comment); // TODO do this right
// this.setState(this.state); // this.setState(this.state);
} else if (op == UserOperation.SaveComment) { } else if (res.op == UserOperation.SaveComment) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found = this.state.replies.find(c => c.id == res.comment.id); let found = this.state.replies.find(c => c.id == data.comment.id);
found.saved = res.comment.saved; found.saved = data.comment.saved;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) { } else if (res.op == UserOperation.CreateCommentLike) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found: Comment = this.state.replies.find( let found: Comment = this.state.replies.find(
c => c.id === res.comment.id c => c.id === data.comment.id
); );
found.score = res.comment.score; found.score = data.comment.score;
found.upvotes = res.comment.upvotes; found.upvotes = data.comment.upvotes;
found.downvotes = res.comment.downvotes; found.downvotes = data.comment.downvotes;
if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote; if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -8,9 +8,10 @@ import {
UserOperation, UserOperation,
PasswordResetForm, PasswordResetForm,
GetSiteResponse, GetSiteResponse,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, validEmail, toast } from '../utils'; import { wsJsonToRes, validEmail, toast } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -292,32 +293,32 @@ export class Login extends Component<any, State> {
WebSocketService.Instance.passwordReset(resetForm); WebSocketService.Instance.passwordReset(resetForm);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state = this.emptyState; this.state = this.emptyState;
this.setState(this.state); this.setState(this.state);
return; return;
} else { } else {
if (op == UserOperation.Login) { if (res.op == UserOperation.Login) {
let data = res.data as LoginResponse;
this.state = this.emptyState; this.state = this.emptyState;
this.setState(this.state); this.setState(this.state);
let res: LoginResponse = msg; UserService.Instance.login(data);
UserService.Instance.login(res);
toast(i18n.t('logged_in')); toast(i18n.t('logged_in'));
this.props.history.push('/'); this.props.history.push('/');
} else if (op == UserOperation.Register) { } else if (res.op == UserOperation.Register) {
let data = res.data as LoginResponse;
this.state = this.emptyState; this.state = this.emptyState;
this.setState(this.state); this.setState(this.state);
let res: LoginResponse = msg; UserService.Instance.login(data);
UserService.Instance.login(res);
this.props.history.push('/communities'); this.props.history.push('/communities');
} else if (op == UserOperation.PasswordReset) { } else if (res.op == UserOperation.PasswordReset) {
toast(i18n.t('reset_password_mail_sent')); toast(i18n.t('reset_password_mail_sent'));
} else if (op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let res: GetSiteResponse = msg; let data = res.data as GetSiteResponse;
this.state.enable_nsfw = res.site.enable_nsfw; this.state.enable_nsfw = data.site.enable_nsfw;
this.setState(this.state); this.setState(this.state);
document.title = `${i18n.t('login')} - ${ document.title = `${i18n.t('login')} - ${
WebSocketService.Instance.site.name WebSocketService.Instance.site.name

View file

@ -17,6 +17,7 @@ import {
CreatePostLikeResponse, CreatePostLikeResponse,
Post, Post,
GetPostsForm, GetPostsForm,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { PostListings } from './post-listings'; import { PostListings } from './post-listings';
@ -24,7 +25,7 @@ import { SortSelect } from './sort-select';
import { ListingTypeSelect } from './listing-type-select'; import { ListingTypeSelect } from './listing-type-select';
import { SiteForm } from './site-form'; import { SiteForm } from './site-form';
import { import {
msgOp, wsJsonToRes,
repoUrl, repoUrl,
mdToHtml, mdToHtml,
fetchLimit, fetchLimit,
@ -57,7 +58,6 @@ export class Main extends Component<any, MainState> {
subscribedCommunities: [], subscribedCommunities: [],
trendingCommunities: [], trendingCommunities: [],
site: { site: {
op: null,
site: { site: {
id: null, id: null,
name: null, name: null,
@ -563,50 +563,50 @@ export class Main extends Component<any, MainState> {
WebSocketService.Instance.getPosts(getPostsForm); WebSocketService.Instance.getPosts(getPostsForm);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetFollowedCommunities) { } else if (res.op == UserOperation.GetFollowedCommunities) {
let res: GetFollowedCommunitiesResponse = msg; let data = res.data as GetFollowedCommunitiesResponse;
this.state.subscribedCommunities = res.communities; this.state.subscribedCommunities = data.communities;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.ListCommunities) { } else if (res.op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg; let data = res.data as ListCommunitiesResponse;
this.state.trendingCommunities = res.communities; this.state.trendingCommunities = data.communities;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let res: GetSiteResponse = msg; let data = res.data as GetSiteResponse;
// This means it hasn't been set up yet // This means it hasn't been set up yet
if (!res.site) { if (!data.site) {
this.context.router.history.push('/setup'); this.context.router.history.push('/setup');
} }
this.state.site.admins = res.admins; this.state.site.admins = data.admins;
this.state.site.site = res.site; this.state.site.site = data.site;
this.state.site.banned = res.banned; this.state.site.banned = data.banned;
this.state.site.online = res.online; this.state.site.online = data.online;
this.setState(this.state); this.setState(this.state);
document.title = `${WebSocketService.Instance.site.name}`; document.title = `${WebSocketService.Instance.site.name}`;
} else if (op == UserOperation.EditSite) { } else if (res.op == UserOperation.EditSite) {
let res: SiteResponse = msg; let data = res.data as SiteResponse;
this.state.site.site = res.site; this.state.site.site = data.site;
this.state.showEditSite = false; this.state.showEditSite = false;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.GetPosts) { } else if (res.op == UserOperation.GetPosts) {
let res: GetPostsResponse = msg; let data = res.data as GetPostsResponse;
this.state.posts = res.posts; this.state.posts = data.posts;
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) { } else if (res.op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg; let data = res.data as CreatePostLikeResponse;
let found = this.state.posts.find(c => c.id == res.post.id); let found = this.state.posts.find(c => c.id == data.post.id);
found.my_vote = res.post.my_vote; found.my_vote = data.post.my_vote;
found.score = res.post.score; found.score = data.post.score;
found.upvotes = res.post.upvotes; found.upvotes = data.post.upvotes;
found.downvotes = res.post.downvotes; found.downvotes = data.post.downvotes;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -17,7 +17,7 @@ import {
ModAdd, ModAdd,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp, addTypeInfo, fetchLimit, toast } from '../utils'; import { wsJsonToRes, addTypeInfo, fetchLimit, toast } from '../utils';
import { MomentTime } from './moment-time'; import { MomentTime } from './moment-time';
import moment from 'moment'; import moment from 'moment';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -422,17 +422,17 @@ export class Modlog extends Component<any, ModlogState> {
WebSocketService.Instance.getModlog(modlogForm); WebSocketService.Instance.getModlog(modlogForm);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetModlog) { } else if (res.op == UserOperation.GetModlog) {
let res: GetModlogResponse = msg; let data = res.data as GetModlogResponse;
this.state.loading = false; this.state.loading = false;
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setCombined(res); this.setCombined(data);
} }
} }
} }

View file

@ -15,9 +15,10 @@ import {
GetSiteResponse, GetSiteResponse,
Comment, Comment,
PrivateMessage, PrivateMessage,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { import {
msgOp, wsJsonToRes,
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
showAvatars, showAvatars,
fetchLimit, fetchLimit,
@ -199,17 +200,17 @@ export class Navbar extends Component<any, NavbarState> {
i.setState(i.state); i.setState(i.state);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
if (msg.error == 'not_logged_in') { if (res.error == 'not_logged_in') {
UserService.Instance.logout(); UserService.Instance.logout();
location.reload(); location.reload();
} }
return; return;
} else if (op == UserOperation.GetReplies) { } else if (res.op == UserOperation.GetReplies) {
let res: GetRepliesResponse = msg; let data = res.data as GetRepliesResponse;
let unreadReplies = res.replies.filter(r => !r.read); let unreadReplies = data.replies.filter(r => !r.read);
if ( if (
unreadReplies.length > 0 && unreadReplies.length > 0 &&
this.state.fetchCount > 1 && this.state.fetchCount > 1 &&
@ -221,9 +222,9 @@ export class Navbar extends Component<any, NavbarState> {
this.state.replies = unreadReplies; this.state.replies = unreadReplies;
this.setState(this.state); this.setState(this.state);
this.sendUnreadCount(); this.sendUnreadCount();
} else if (op == UserOperation.GetUserMentions) { } else if (res.op == UserOperation.GetUserMentions) {
let res: GetUserMentionsResponse = msg; let data = res.data as GetUserMentionsResponse;
let unreadMentions = res.mentions.filter(r => !r.read); let unreadMentions = data.mentions.filter(r => !r.read);
if ( if (
unreadMentions.length > 0 && unreadMentions.length > 0 &&
this.state.fetchCount > 1 && this.state.fetchCount > 1 &&
@ -235,9 +236,9 @@ export class Navbar extends Component<any, NavbarState> {
this.state.mentions = unreadMentions; this.state.mentions = unreadMentions;
this.setState(this.state); this.setState(this.state);
this.sendUnreadCount(); this.sendUnreadCount();
} else if (op == UserOperation.GetPrivateMessages) { } else if (res.op == UserOperation.GetPrivateMessages) {
let res: PrivateMessagesResponse = msg; let data = res.data as PrivateMessagesResponse;
let unreadMessages = res.messages.filter(r => !r.read); let unreadMessages = data.messages.filter(r => !r.read);
if ( if (
unreadMessages.length > 0 && unreadMessages.length > 0 &&
this.state.fetchCount > 1 && this.state.fetchCount > 1 &&
@ -249,12 +250,12 @@ export class Navbar extends Component<any, NavbarState> {
this.state.messages = unreadMessages; this.state.messages = unreadMessages;
this.setState(this.state); this.setState(this.state);
this.sendUnreadCount(); this.sendUnreadCount();
} else if (op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let res: GetSiteResponse = msg; let data = res.data as GetSiteResponse;
if (res.site) { if (data.site) {
this.state.siteName = res.site.name; this.state.siteName = data.site.name;
WebSocketService.Instance.site = res.site; WebSocketService.Instance.site = data.site;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -5,9 +5,10 @@ import {
UserOperation, UserOperation,
LoginResponse, LoginResponse,
PasswordChangeForm, PasswordChangeForm,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, capitalizeFirstLetter, toast } from '../utils'; import { wsJsonToRes, capitalizeFirstLetter, toast } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -133,19 +134,19 @@ export class PasswordChange extends Component<any, State> {
WebSocketService.Instance.passwordChange(i.state.passwordChangeForm); WebSocketService.Instance.passwordChange(i.state.passwordChangeForm);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;
} else { } else {
if (op == UserOperation.PasswordChange) { if (res.op == UserOperation.PasswordChange) {
let data = res.data as LoginResponse;
this.state = this.emptyState; this.state = this.emptyState;
this.setState(this.state); this.setState(this.state);
let res: LoginResponse = msg; UserService.Instance.login(data);
UserService.Instance.login(res);
this.props.history.push('/'); this.props.history.push('/');
} }
} }

View file

@ -16,10 +16,11 @@ import {
SearchType, SearchType,
SearchResponse, SearchResponse,
GetSiteResponse, GetSiteResponse,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { import {
msgOp, wsJsonToRes,
getPageTitle, getPageTitle,
validURL, validURL,
capitalizeFirstLetter, capitalizeFirstLetter,
@ -458,47 +459,47 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
}); });
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;
} else if (op == UserOperation.ListCommunities) { } else if (res.op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg; let data = res.data as ListCommunitiesResponse;
this.state.communities = res.communities; this.state.communities = data.communities;
if (this.props.post) { if (this.props.post) {
this.state.postForm.community_id = this.props.post.community_id; this.state.postForm.community_id = this.props.post.community_id;
} else if (this.props.params && this.props.params.community) { } else if (this.props.params && this.props.params.community) {
let foundCommunityId = res.communities.find( let foundCommunityId = data.communities.find(
r => r.name == this.props.params.community r => r.name == this.props.params.community
).id; ).id;
this.state.postForm.community_id = foundCommunityId; this.state.postForm.community_id = foundCommunityId;
} else { } else {
this.state.postForm.community_id = res.communities[0].id; this.state.postForm.community_id = data.communities[0].id;
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePost) { } else if (res.op == UserOperation.CreatePost) {
let data = res.data as PostResponse;
this.state.loading = false; this.state.loading = false;
let res: PostResponse = msg; this.props.onCreate(data.post.id);
this.props.onCreate(res.post.id); } else if (res.op == UserOperation.EditPost) {
} else if (op == UserOperation.EditPost) { let data = res.data as PostResponse;
this.state.loading = false; this.state.loading = false;
let res: PostResponse = msg; this.props.onEdit(data.post);
this.props.onEdit(res.post); } else if (res.op == UserOperation.Search) {
} else if (op == UserOperation.Search) { let data = res.data as SearchResponse;
let res: SearchResponse = msg;
if (res.type_ == SearchType[SearchType.Posts]) { if (data.type_ == SearchType[SearchType.Posts]) {
this.state.suggestedPosts = res.posts; this.state.suggestedPosts = data.posts;
} else if (res.type_ == SearchType[SearchType.Url]) { } else if (data.type_ == SearchType[SearchType.Url]) {
this.state.crossPosts = res.posts; this.state.crossPosts = data.posts;
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let res: GetSiteResponse = msg; let data = res.data as GetSiteResponse;
this.state.enable_nsfw = res.site.enable_nsfw; this.state.enable_nsfw = data.site.enable_nsfw;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -26,9 +26,10 @@ import {
SearchResponse, SearchResponse,
GetSiteResponse, GetSiteResponse,
GetCommunityResponse, GetCommunityResponse,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, hotRank, toast } from '../utils'; import { wsJsonToRes, hotRank, toast } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { PostListings } from './post-listings'; import { PostListings } from './post-listings';
import { Sidebar } from './sidebar'; import { Sidebar } from './sidebar';
@ -341,19 +342,19 @@ export class Post extends Component<any, PostState> {
); );
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetPost) { } else if (res.op == UserOperation.GetPost) {
let res: GetPostResponse = msg; let data = res.data as GetPostResponse;
this.state.post = res.post; this.state.post = data.post;
this.state.comments = res.comments; this.state.comments = data.comments;
this.state.community = res.community; this.state.community = data.community;
this.state.moderators = res.moderators; this.state.moderators = data.moderators;
this.state.admins = res.admins; this.state.admins = data.admins;
this.state.loading = false; this.state.loading = false;
document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`; document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`;
@ -370,111 +371,111 @@ export class Post extends Component<any, PostState> {
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateComment) { } else if (res.op == UserOperation.CreateComment) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
this.state.comments.unshift(res.comment); this.state.comments.unshift(data.comment);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.EditComment) { } else if (res.op == UserOperation.EditComment) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found = this.state.comments.find(c => c.id == res.comment.id); let found = this.state.comments.find(c => c.id == data.comment.id);
found.content = res.comment.content; found.content = data.comment.content;
found.updated = res.comment.updated; found.updated = data.comment.updated;
found.removed = res.comment.removed; found.removed = data.comment.removed;
found.deleted = res.comment.deleted; found.deleted = data.comment.deleted;
found.upvotes = res.comment.upvotes; found.upvotes = data.comment.upvotes;
found.downvotes = res.comment.downvotes; found.downvotes = data.comment.downvotes;
found.score = res.comment.score; found.score = data.comment.score;
found.read = res.comment.read; found.read = data.comment.read;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.SaveComment) { } else if (res.op == UserOperation.SaveComment) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found = this.state.comments.find(c => c.id == res.comment.id); let found = this.state.comments.find(c => c.id == data.comment.id);
found.saved = res.comment.saved; found.saved = data.comment.saved;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) { } else if (res.op == UserOperation.CreateCommentLike) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found: Comment = this.state.comments.find( let found: Comment = this.state.comments.find(
c => c.id === res.comment.id c => c.id === data.comment.id
); );
found.score = res.comment.score; found.score = data.comment.score;
found.upvotes = res.comment.upvotes; found.upvotes = data.comment.upvotes;
found.downvotes = res.comment.downvotes; found.downvotes = data.comment.downvotes;
if (res.comment.my_vote !== null) { if (data.comment.my_vote !== null) {
found.my_vote = res.comment.my_vote; found.my_vote = data.comment.my_vote;
found.upvoteLoading = false; found.upvoteLoading = false;
found.downvoteLoading = false; found.downvoteLoading = false;
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) { } else if (res.op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg; let data = res.data as CreatePostLikeResponse;
this.state.post.my_vote = res.post.my_vote; this.state.post.my_vote = data.post.my_vote;
this.state.post.score = res.post.score; this.state.post.score = data.post.score;
this.state.post.upvotes = res.post.upvotes; this.state.post.upvotes = data.post.upvotes;
this.state.post.downvotes = res.post.downvotes; this.state.post.downvotes = data.post.downvotes;
this.state.post.upvoteLoading = false;
this.state.post.downvoteLoading = false;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.EditPost) { } else if (res.op == UserOperation.EditPost) {
let res: PostResponse = msg; let data = res.data as PostResponse;
this.state.post = res.post; this.state.post = data.post;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.SavePost) { } else if (res.op == UserOperation.SavePost) {
let res: PostResponse = msg; let data = res.data as PostResponse;
this.state.post = res.post; this.state.post = data.post;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.EditCommunity) { } else if (res.op == UserOperation.EditCommunity) {
let res: CommunityResponse = msg; let data = res.data as CommunityResponse;
this.state.community = res.community; this.state.community = data.community;
this.state.post.community_id = res.community.id; this.state.post.community_id = data.community.id;
this.state.post.community_name = res.community.name; this.state.post.community_name = data.community.name;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.FollowCommunity) { } else if (res.op == UserOperation.FollowCommunity) {
let res: CommunityResponse = msg; let data = res.data as CommunityResponse;
this.state.community.subscribed = res.community.subscribed; this.state.community.subscribed = data.community.subscribed;
this.state.community.number_of_subscribers = this.state.community.number_of_subscribers =
res.community.number_of_subscribers; data.community.number_of_subscribers;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.BanFromCommunity) { } else if (res.op == UserOperation.BanFromCommunity) {
let res: BanFromCommunityResponse = msg; let data = res.data as BanFromCommunityResponse;
this.state.comments this.state.comments
.filter(c => c.creator_id == res.user.id) .filter(c => c.creator_id == data.user.id)
.forEach(c => (c.banned_from_community = res.banned)); .forEach(c => (c.banned_from_community = data.banned));
if (this.state.post.creator_id == res.user.id) { if (this.state.post.creator_id == data.user.id) {
this.state.post.banned_from_community = res.banned; this.state.post.banned_from_community = data.banned;
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.AddModToCommunity) { } else if (res.op == UserOperation.AddModToCommunity) {
let res: AddModToCommunityResponse = msg; let data = res.data as AddModToCommunityResponse;
this.state.moderators = res.moderators; this.state.moderators = data.moderators;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.BanUser) { } else if (res.op == UserOperation.BanUser) {
let res: BanUserResponse = msg; let data = res.data as BanUserResponse;
this.state.comments this.state.comments
.filter(c => c.creator_id == res.user.id) .filter(c => c.creator_id == data.user.id)
.forEach(c => (c.banned = res.banned)); .forEach(c => (c.banned = data.banned));
if (this.state.post.creator_id == res.user.id) { if (this.state.post.creator_id == data.user.id) {
this.state.post.banned = res.banned; this.state.post.banned = data.banned;
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.AddAdmin) { } else if (res.op == UserOperation.AddAdmin) {
let res: AddAdminResponse = msg; let data = res.data as AddAdminResponse;
this.state.admins = res.admins; this.state.admins = data.admins;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.Search) { } else if (res.op == UserOperation.Search) {
let res: SearchResponse = msg; let data = res.data as SearchResponse;
this.state.crossPosts = res.posts.filter(p => p.id != this.state.post.id); this.state.crossPosts = data.posts.filter(
p => p.id != this.state.post.id
);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.TransferSite) { } else if (res.op == UserOperation.TransferSite) {
let res: GetSiteResponse = msg; let data = res.data as GetSiteResponse;
this.state.admins = res.admins; this.state.admins = data.admins;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.TransferCommunity) { } else if (res.op == UserOperation.TransferCommunity) {
let res: GetCommunityResponse = msg; let data = res.data as GetCommunityResponse;
this.state.community = res.community; this.state.community = data.community;
this.state.moderators = res.moderators; this.state.moderators = data.moderators;
this.state.admins = res.admins; this.state.admins = data.admins;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -13,15 +13,16 @@ import {
UserDetailsResponse, UserDetailsResponse,
GetUserDetailsForm, GetUserDetailsForm,
SortType, SortType,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { import {
msgOp,
capitalizeFirstLetter, capitalizeFirstLetter,
markdownHelpUrl, markdownHelpUrl,
mdToHtml, mdToHtml,
showAvatars, showAvatars,
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
wsJsonToRes,
toast, toast,
} from '../utils'; } from '../utils';
import autosize from 'autosize'; import autosize from 'autosize';
@ -266,26 +267,26 @@ export class PrivateMessageForm extends Component<
i.setState(i.state); i.setState(i.state);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;
} else if (op == UserOperation.EditPrivateMessage) { } else if (res.op == UserOperation.EditPrivateMessage) {
let data = res.data as PrivateMessageResponse;
this.state.loading = false; this.state.loading = false;
let res: PrivateMessageResponse = msg; this.props.onEdit(data.message);
this.props.onEdit(res.message); } else if (res.op == UserOperation.GetUserDetails) {
} else if (op == UserOperation.GetUserDetails) { let data = res.data as UserDetailsResponse;
let res: UserDetailsResponse = msg; this.state.recipient = data.user;
this.state.recipient = res.user; this.state.privateMessageForm.recipient_id = data.user.id;
this.state.privateMessageForm.recipient_id = res.user.id;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePrivateMessage) { } else if (res.op == UserOperation.CreatePrivateMessage) {
let data = res.data as PrivateMessageResponse;
this.state.loading = false; this.state.loading = false;
let res: PrivateMessageResponse = msg; this.props.onCreate(data.message);
this.props.onCreate(res.message);
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -14,10 +14,11 @@ import {
SearchType, SearchType,
CreatePostLikeResponse, CreatePostLikeResponse,
CommentResponse, CommentResponse,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { import {
msgOp, wsJsonToRes,
fetchLimit, fetchLimit,
routeSearchTypeToEnum, routeSearchTypeToEnum,
routeSortTypeToEnum, routeSortTypeToEnum,
@ -48,7 +49,6 @@ export class Search extends Component<any, SearchState> {
sort: this.getSortTypeFromProps(this.props), sort: this.getSortTypeFromProps(this.props),
page: this.getPageFromProps(this.props), page: this.getPageFromProps(this.props),
searchResponse: { searchResponse: {
op: null,
type_: null, type_: null,
posts: [], posts: [],
comments: [], comments: [],
@ -401,7 +401,6 @@ export class Search extends Component<any, SearchState> {
return ( return (
<div> <div>
{res && {res &&
res.op &&
res.posts.length == 0 && res.posts.length == 0 &&
res.comments.length == 0 && res.comments.length == 0 &&
res.communities.length == 0 && res.communities.length == 0 &&
@ -477,44 +476,44 @@ export class Search extends Component<any, SearchState> {
); );
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.Search) { } else if (res.op == UserOperation.Search) {
let res: SearchResponse = msg; let data = res.data as SearchResponse;
this.state.searchResponse = res; this.state.searchResponse = data;
this.state.loading = false; this.state.loading = false;
document.title = `${i18n.t('search')} - ${this.state.q} - ${ document.title = `${i18n.t('search')} - ${this.state.q} - ${
WebSocketService.Instance.site.name WebSocketService.Instance.site.name
}`; }`;
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) { } else if (res.op == UserOperation.CreateCommentLike) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found: Comment = this.state.searchResponse.comments.find( let found: Comment = this.state.searchResponse.comments.find(
c => c.id === res.comment.id c => c.id === data.comment.id
); );
found.score = res.comment.score; found.score = data.comment.score;
found.upvotes = res.comment.upvotes; found.upvotes = data.comment.upvotes;
found.downvotes = res.comment.downvotes; found.downvotes = data.comment.downvotes;
if (res.comment.my_vote !== null) { if (data.comment.my_vote !== null) {
found.my_vote = res.comment.my_vote; found.my_vote = data.comment.my_vote;
found.upvoteLoading = false; found.upvoteLoading = false;
found.downvoteLoading = false; found.downvoteLoading = false;
} }
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) { } else if (res.op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg; let data = res.data as CreatePostLikeResponse;
let found = this.state.searchResponse.posts.find( let found = this.state.searchResponse.posts.find(
c => c.id == res.post.id c => c.id == data.post.id
); );
found.my_vote = res.post.my_vote; found.my_vote = data.post.my_vote;
found.score = res.post.score; found.score = data.post.score;
found.upvotes = res.post.upvotes; found.upvotes = data.post.upvotes;
found.downvotes = res.post.downvotes; found.downvotes = data.post.downvotes;
this.setState(this.state); this.setState(this.state);
} }
} }

View file

@ -1,9 +1,14 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { RegisterForm, LoginResponse, UserOperation } from '../interfaces'; import {
RegisterForm,
LoginResponse,
UserOperation,
WebSocketJsonResponse,
} from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, toast } from '../utils'; import { wsJsonToRes, toast } from '../utils';
import { SiteForm } from './site-form'; import { SiteForm } from './site-form';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -181,21 +186,20 @@ export class Setup extends Component<any, State> {
i.setState(i.state); i.setState(i.state);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.userLoading = false; this.state.userLoading = false;
this.setState(this.state); this.setState(this.state);
return; return;
} else if (op == UserOperation.Register) { } else if (res.op == UserOperation.Register) {
let data = res.data as LoginResponse;
this.state.userLoading = false; this.state.userLoading = false;
this.state.doneRegisteringUser = true; this.state.doneRegisteringUser = true;
let res: LoginResponse = msg; UserService.Instance.login(data);
UserService.Instance.login(res);
console.log(res);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateSite) { } else if (res.op == UserOperation.CreateSite) {
this.props.history.push('/'); this.props.history.push('/');
} }
} }

View file

@ -19,10 +19,11 @@ import {
AddAdminResponse, AddAdminResponse,
DeleteAccountForm, DeleteAccountForm,
CreatePostLikeResponse, CreatePostLikeResponse,
WebSocketJsonResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { import {
msgOp, wsJsonToRes,
fetchLimit, fetchLimit,
routeSortTypeToEnum, routeSortTypeToEnum,
capitalizeFirstLetter, capitalizeFirstLetter,
@ -1012,27 +1013,27 @@ export class User extends Component<any, UserState> {
WebSocketService.Instance.deleteAccount(i.state.deleteAccountForm); WebSocketService.Instance.deleteAccount(i.state.deleteAccountForm);
} }
parseMessage(msg: any) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let res = wsJsonToRes(msg);
if (msg.error) { if (res.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.deleteAccountLoading = false; this.state.deleteAccountLoading = false;
this.state.avatarLoading = false; this.state.avatarLoading = false;
this.state.userSettingsLoading = false; this.state.userSettingsLoading = false;
if (msg.error == 'couldnt_find_that_username_or_email') { if (res.error == 'couldnt_find_that_username_or_email') {
this.context.router.history.push('/'); this.context.router.history.push('/');
} }
this.setState(this.state); this.setState(this.state);
return; return;
} else if (op == UserOperation.GetUserDetails) { } else if (res.op == UserOperation.GetUserDetails) {
let res: UserDetailsResponse = msg; let data = res.data as UserDetailsResponse;
this.state.user = res.user; this.state.user = data.user;
this.state.comments = res.comments; this.state.comments = data.comments;
this.state.follows = res.follows; this.state.follows = data.follows;
this.state.moderates = res.moderates; this.state.moderates = data.moderates;
this.state.posts = res.posts; this.state.posts = data.posts;
this.state.admins = res.admins; this.state.admins = data.admins;
this.state.loading = false; this.state.loading = false;
if (this.isCurrentUser) { if (this.isCurrentUser) {
this.state.userSettingsForm.show_nsfw = this.state.userSettingsForm.show_nsfw =
@ -1055,67 +1056,67 @@ export class User extends Component<any, UserState> {
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`; document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.EditComment) { } else if (res.op == UserOperation.EditComment) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found = this.state.comments.find(c => c.id == res.comment.id); let found = this.state.comments.find(c => c.id == data.comment.id);
found.content = res.comment.content; found.content = data.comment.content;
found.updated = res.comment.updated; found.updated = data.comment.updated;
found.removed = res.comment.removed; found.removed = data.comment.removed;
found.deleted = res.comment.deleted; found.deleted = data.comment.deleted;
found.upvotes = res.comment.upvotes; found.upvotes = data.comment.upvotes;
found.downvotes = res.comment.downvotes; found.downvotes = data.comment.downvotes;
found.score = res.comment.score; found.score = data.comment.score;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateComment) { } else if (res.op == UserOperation.CreateComment) {
// let res: CommentResponse = msg; // let res: CommentResponse = msg;
toast(i18n.t('reply_sent')); toast(i18n.t('reply_sent'));
// this.state.comments.unshift(res.comment); // TODO do this right // this.state.comments.unshift(res.comment); // TODO do this right
// this.setState(this.state); // this.setState(this.state);
} else if (op == UserOperation.SaveComment) { } else if (res.op == UserOperation.SaveComment) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found = this.state.comments.find(c => c.id == res.comment.id); let found = this.state.comments.find(c => c.id == data.comment.id);
found.saved = res.comment.saved; found.saved = data.comment.saved;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) { } else if (res.op == UserOperation.CreateCommentLike) {
let res: CommentResponse = msg; let data = res.data as CommentResponse;
let found: Comment = this.state.comments.find( let found: Comment = this.state.comments.find(
c => c.id === res.comment.id c => c.id === data.comment.id
); );
found.score = res.comment.score; found.score = data.comment.score;
found.upvotes = res.comment.upvotes; found.upvotes = data.comment.upvotes;
found.downvotes = res.comment.downvotes; found.downvotes = data.comment.downvotes;
if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote; if (data.comment.my_vote !== null) found.my_vote = data.comment.my_vote;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) { } else if (res.op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg; let data = res.data as CreatePostLikeResponse;
let found = this.state.posts.find(c => c.id == res.post.id); let found = this.state.posts.find(c => c.id == data.post.id);
found.my_vote = res.post.my_vote; found.my_vote = data.post.my_vote;
found.score = res.post.score; found.score = data.post.score;
found.upvotes = res.post.upvotes; found.upvotes = data.post.upvotes;
found.downvotes = res.post.downvotes; found.downvotes = data.post.downvotes;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.BanUser) { } else if (res.op == UserOperation.BanUser) {
let res: BanUserResponse = msg; let data = res.data as BanUserResponse;
this.state.comments this.state.comments
.filter(c => c.creator_id == res.user.id) .filter(c => c.creator_id == data.user.id)
.forEach(c => (c.banned = res.banned)); .forEach(c => (c.banned = data.banned));
this.state.posts this.state.posts
.filter(c => c.creator_id == res.user.id) .filter(c => c.creator_id == data.user.id)
.forEach(c => (c.banned = res.banned)); .forEach(c => (c.banned = data.banned));
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.AddAdmin) { } else if (res.op == UserOperation.AddAdmin) {
let res: AddAdminResponse = msg; let data = res.data as AddAdminResponse;
this.state.admins = res.admins; this.state.admins = data.admins;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.SaveUserSettings) { } else if (res.op == UserOperation.SaveUserSettings) {
let data = res.data as LoginResponse;
this.state = this.emptyState; this.state = this.emptyState;
this.state.userSettingsLoading = false; this.state.userSettingsLoading = false;
this.setState(this.state); this.setState(this.state);
let res: LoginResponse = msg; UserService.Instance.login(data);
UserService.Instance.login(res); } else if (res.op == UserOperation.DeleteAccount) {
} else if (op == UserOperation.DeleteAccount) {
this.state.deleteAccountLoading = false; this.state.deleteAccountLoading = false;
this.state.deleteAccountShowConfirm = false; this.state.deleteAccountShowConfirm = false;
this.setState(this.state); this.setState(this.state);

64
ui/src/interfaces.ts vendored
View file

@ -249,7 +249,6 @@ export interface FollowCommunityForm {
} }
export interface GetFollowedCommunitiesResponse { export interface GetFollowedCommunitiesResponse {
op: string;
communities: Array<CommunityUser>; communities: Array<CommunityUser>;
} }
@ -264,7 +263,6 @@ export interface GetUserDetailsForm {
} }
export interface UserDetailsResponse { export interface UserDetailsResponse {
op: string;
user: UserView; user: UserView;
follows: Array<CommunityUser>; follows: Array<CommunityUser>;
moderates: Array<CommunityUser>; moderates: Array<CommunityUser>;
@ -282,7 +280,6 @@ export interface GetRepliesForm {
} }
export interface GetRepliesResponse { export interface GetRepliesResponse {
op: string;
replies: Array<Comment>; replies: Array<Comment>;
} }
@ -295,7 +292,6 @@ export interface GetUserMentionsForm {
} }
export interface GetUserMentionsResponse { export interface GetUserMentionsResponse {
op: string;
mentions: Array<Comment>; mentions: Array<Comment>;
} }
@ -306,7 +302,6 @@ export interface EditUserMentionForm {
} }
export interface UserMentionResponse { export interface UserMentionResponse {
op: string;
mention: Comment; mention: Comment;
} }
@ -320,7 +315,6 @@ export interface BanFromCommunityForm {
} }
export interface BanFromCommunityResponse { export interface BanFromCommunityResponse {
op: string;
user: UserView; user: UserView;
banned: boolean; banned: boolean;
} }
@ -344,7 +338,6 @@ export interface TransferSiteForm {
} }
export interface AddModToCommunityResponse { export interface AddModToCommunityResponse {
op: string;
moderators: Array<CommunityUser>; moderators: Array<CommunityUser>;
} }
@ -356,7 +349,6 @@ export interface GetModlogForm {
} }
export interface GetModlogResponse { export interface GetModlogResponse {
op: string;
removed_posts: Array<ModRemovePost>; removed_posts: Array<ModRemovePost>;
locked_posts: Array<ModLockPost>; locked_posts: Array<ModLockPost>;
stickied_posts: Array<ModStickyPost>; stickied_posts: Array<ModStickyPost>;
@ -497,7 +489,6 @@ export interface RegisterForm {
} }
export interface LoginResponse { export interface LoginResponse {
op: string;
jwt: string; jwt: string;
} }
@ -533,14 +524,12 @@ export interface CommunityForm {
} }
export interface GetCommunityResponse { export interface GetCommunityResponse {
op: string;
community: Community; community: Community;
moderators: Array<CommunityUser>; moderators: Array<CommunityUser>;
admins: Array<UserView>; admins: Array<UserView>;
} }
export interface CommunityResponse { export interface CommunityResponse {
op: string;
community: Community; community: Community;
} }
@ -552,12 +541,10 @@ export interface ListCommunitiesForm {
} }
export interface ListCommunitiesResponse { export interface ListCommunitiesResponse {
op: string;
communities: Array<Community>; communities: Array<Community>;
} }
export interface ListCategoriesResponse { export interface ListCategoriesResponse {
op: string;
categories: Array<Category>; categories: Array<Category>;
} }
@ -586,7 +573,6 @@ export interface PostFormParams {
} }
export interface GetPostResponse { export interface GetPostResponse {
op: string;
post: Post; post: Post;
comments: Array<Comment>; comments: Array<Comment>;
community: Community; community: Community;
@ -601,7 +587,6 @@ export interface SavePostForm {
} }
export interface PostResponse { export interface PostResponse {
op: string;
post: Post; post: Post;
} }
@ -625,7 +610,6 @@ export interface SaveCommentForm {
} }
export interface CommentResponse { export interface CommentResponse {
op: string;
comment: Comment; comment: Comment;
} }
@ -651,7 +635,6 @@ export interface GetPostsForm {
} }
export interface GetPostsResponse { export interface GetPostsResponse {
op: string;
posts: Array<Post>; posts: Array<Post>;
} }
@ -662,7 +645,6 @@ export interface CreatePostLikeForm {
} }
export interface CreatePostLikeResponse { export interface CreatePostLikeResponse {
op: string;
post: Post; post: Post;
} }
@ -676,7 +658,6 @@ export interface SiteForm {
} }
export interface GetSiteResponse { export interface GetSiteResponse {
op: string;
site: Site; site: Site;
admins: Array<UserView>; admins: Array<UserView>;
banned: Array<UserView>; banned: Array<UserView>;
@ -684,7 +665,6 @@ export interface GetSiteResponse {
} }
export interface SiteResponse { export interface SiteResponse {
op: string;
site: Site; site: Site;
} }
@ -697,7 +677,6 @@ export interface BanUserForm {
} }
export interface BanUserResponse { export interface BanUserResponse {
op: string;
user: UserView; user: UserView;
banned: boolean; banned: boolean;
} }
@ -709,7 +688,6 @@ export interface AddAdminForm {
} }
export interface AddAdminResponse { export interface AddAdminResponse {
op: string;
admins: Array<UserView>; admins: Array<UserView>;
} }
@ -724,7 +702,6 @@ export interface SearchForm {
} }
export interface SearchResponse { export interface SearchResponse {
op: string;
type_: string; type_: string;
posts?: Array<Post>; posts?: Array<Post>;
comments?: Array<Comment>; comments?: Array<Comment>;
@ -740,9 +717,8 @@ export interface PasswordResetForm {
email: string; email: string;
} }
export interface PasswordResetResponse { // export interface PasswordResetResponse {
op: string; // }
}
export interface PasswordChangeForm { export interface PasswordChangeForm {
token: string; token: string;
@ -776,11 +752,43 @@ export interface GetPrivateMessagesForm {
} }
export interface PrivateMessagesResponse { export interface PrivateMessagesResponse {
op: string;
messages: Array<PrivateMessage>; messages: Array<PrivateMessage>;
} }
export interface PrivateMessageResponse { export interface PrivateMessageResponse {
op: string;
message: PrivateMessage; message: PrivateMessage;
} }
type ResponseType =
| SiteResponse
| GetFollowedCommunitiesResponse
| ListCommunitiesResponse
| GetPostsResponse
| CreatePostLikeResponse
| GetRepliesResponse
| GetUserMentionsResponse
| ListCategoriesResponse
| CommunityResponse
| CommentResponse
| UserMentionResponse
| LoginResponse
| GetModlogResponse
| SearchResponse
| BanFromCommunityResponse
| AddModToCommunityResponse
| BanUserResponse
| AddAdminResponse
| PrivateMessageResponse
| PrivateMessagesResponse;
export interface WebSocketResponse {
op: UserOperation;
data: ResponseType;
error?: string;
}
export interface WebSocketJsonResponse {
op: string;
data: ResponseType;
error?: string;
}

9
ui/src/utils.ts vendored
View file

@ -16,6 +16,8 @@ import {
SortType, SortType,
ListingType, ListingType,
SearchType, SearchType,
WebSocketResponse,
WebSocketJsonResponse,
} from './interfaces'; } from './interfaces';
import { UserService } from './services/UserService'; import { UserService } from './services/UserService';
import markdown_it from 'markdown-it'; import markdown_it from 'markdown-it';
@ -40,9 +42,12 @@ export function randomStr() {
.substr(2, 10); .substr(2, 10);
} }
export function msgOp(msg: any): UserOperation { export function wsJsonToRes(msg: WebSocketJsonResponse): WebSocketResponse {
let opStr: string = msg.op; let opStr: string = msg.op;
return UserOperation[opStr]; return {
op: UserOperation[opStr],
data: msg.data,
};
} }
export const md = new markdown_it({ export const md = new markdown_it({