From 353e2e027a6428aa98c1bb6465beea879c68be8a Mon Sep 17 00:00:00 2001
From: Felix Ableitner <me@nutomic.com>
Date: Tue, 1 Sep 2020 16:25:34 +0200
Subject: [PATCH] Move api structs and rate limit into separate workspaces

---
 server/Cargo.lock                             |  27 ++
 server/Cargo.toml                             |   5 +-
 server/lemmy_api_structs/Cargo.toml           |  10 +
 server/lemmy_api_structs/src/comment.rs       |  77 ++++++
 server/lemmy_api_structs/src/community.rs     | 131 ++++++++++
 server/lemmy_api_structs/src/lib.rs           |  24 ++
 server/lemmy_api_structs/src/post.rs          | 105 ++++++++
 server/lemmy_api_structs/src/site.rs          | 127 +++++++++
 server/lemmy_api_structs/src/user.rs          | 239 +++++++++++++++++
 server/lemmy_rate_limit/Cargo.toml            |  15 ++
 .../mod.rs => lemmy_rate_limit/src/lib.rs}    |  11 +-
 .../src}/rate_limiter.rs                      |   7 +-
 server/lemmy_utils/Cargo.toml                 |   2 +
 server/lemmy_utils/src/lib.rs                 |  41 +++
 server/src/api/comment.rs                     |  82 +-----
 server/src/api/community.rs                   | 133 +---------
 server/src/api/mod.rs                         |  20 +-
 server/src/api/post.rs                        | 112 +-------
 server/src/api/site.rs                        | 125 +--------
 server/src/api/user.rs                        | 246 +-----------------
 server/src/apub/activities.rs                 |   3 +-
 server/src/apub/activity_queue.rs             |   7 +-
 server/src/apub/comment.rs                    |   2 +-
 server/src/apub/community.rs                  |   3 +-
 .../src/apub/extensions/group_extensions.rs   |   2 +-
 server/src/apub/extensions/signatures.rs      |   4 +-
 server/src/apub/fetcher.rs                    |   5 +-
 server/src/apub/inbox/activities/announce.rs  |   3 +-
 server/src/apub/inbox/activities/create.rs    |   9 +-
 server/src/apub/inbox/activities/delete.rs    |   9 +-
 server/src/apub/inbox/activities/dislike.rs   |   5 +-
 server/src/apub/inbox/activities/like.rs      |   5 +-
 server/src/apub/inbox/activities/remove.rs    |   9 +-
 server/src/apub/inbox/activities/undo.rs      |   9 +-
 server/src/apub/inbox/activities/update.rs    |   9 +-
 server/src/apub/inbox/community_inbox.rs      |   3 +-
 server/src/apub/inbox/shared_inbox.rs         |   3 +-
 server/src/apub/inbox/user_inbox.rs           |   5 +-
 server/src/apub/mod.rs                        |   2 +-
 server/src/apub/post.rs                       |   3 +-
 server/src/apub/private_message.rs            |   3 +-
 server/src/apub/user.rs                       |   3 +-
 server/src/code_migrations.rs                 |   2 +-
 server/src/lib.rs                             |  48 +---
 server/src/main.rs                            |   5 +-
 server/src/request.rs                         |   2 +-
 server/src/routes/api.rs                      |   8 +-
 server/src/routes/feeds.rs                    |   4 +-
 server/src/routes/images.rs                   |   2 +-
 server/src/routes/nodeinfo.rs                 |   4 +-
 server/src/routes/webfinger.rs                |   9 +-
 server/src/routes/websocket.rs                |   2 +-
 server/src/websocket/chat_server.rs           |  12 +-
 server/src/websocket/handlers.rs              |   6 +-
 server/src/websocket/messages.rs              |  12 +-
 55 files changed, 918 insertions(+), 843 deletions(-)
 create mode 100644 server/lemmy_api_structs/Cargo.toml
 create mode 100644 server/lemmy_api_structs/src/comment.rs
 create mode 100644 server/lemmy_api_structs/src/community.rs
 create mode 100644 server/lemmy_api_structs/src/lib.rs
 create mode 100644 server/lemmy_api_structs/src/post.rs
 create mode 100644 server/lemmy_api_structs/src/site.rs
 create mode 100644 server/lemmy_api_structs/src/user.rs
 create mode 100644 server/lemmy_rate_limit/Cargo.toml
 rename server/{src/rate_limit/mod.rs => lemmy_rate_limit/src/lib.rs} (96%)
 rename server/{src/rate_limit => lemmy_rate_limit/src}/rate_limiter.rs (94%)

diff --git a/server/Cargo.lock b/server/Cargo.lock
index 815ebc0a..305af342 100644
--- a/server/Cargo.lock
+++ b/server/Cargo.lock
@@ -1794,6 +1794,15 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
+[[package]]
+name = "lemmy_api_structs"
+version = "0.1.0"
+dependencies = [
+ "lemmy_db",
+ "serde 1.0.114",
+ "thiserror",
+]
+
 [[package]]
 name = "lemmy_db"
 version = "0.1.0"
@@ -1812,6 +1821,20 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "lemmy_rate_limit"
+version = "0.1.0"
+dependencies = [
+ "actix-web",
+ "futures",
+ "lemmy_api_structs",
+ "lemmy_utils",
+ "log",
+ "strum",
+ "strum_macros",
+ "tokio",
+]
+
 [[package]]
 name = "lemmy_server"
 version = "0.0.1"
@@ -1841,7 +1864,9 @@ dependencies = [
  "itertools",
  "jsonwebtoken",
  "lazy_static",
+ "lemmy_api_structs",
  "lemmy_db",
+ "lemmy_rate_limit",
  "lemmy_utils",
  "log",
  "openssl",
@@ -1864,6 +1889,8 @@ dependencies = [
 name = "lemmy_utils"
 version = "0.1.0"
 dependencies = [
+ "actix-web",
+ "anyhow",
  "chrono",
  "comrak",
  "config",
diff --git a/server/Cargo.toml b/server/Cargo.toml
index c5bf9c88..912d130d 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -9,12 +9,15 @@ lto = true
 [workspace]
 members = [
     "lemmy_utils",
-    "lemmy_db"
+    "lemmy_db",
+    "lemmy_api_structs",
 ]
 
 [dependencies]
 lemmy_utils = { path = "./lemmy_utils" }
 lemmy_db = { path = "./lemmy_db" }
+lemmy_api_structs = { path = "./lemmy_api_structs" }
+lemmy_rate_limit = { path = "./lemmy_rate_limit" }
 diesel = "1.4.4"
 diesel_migrations = "1.4.0"
 dotenv = "0.15.0"
diff --git a/server/lemmy_api_structs/Cargo.toml b/server/lemmy_api_structs/Cargo.toml
new file mode 100644
index 00000000..d1695eca
--- /dev/null
+++ b/server/lemmy_api_structs/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "lemmy_api_structs"
+version = "0.1.0"
+authors = ["Felix Ableitner <me@nutomic.com>"]
+edition = "2018"
+
+[dependencies]
+lemmy_db = { path = "../lemmy_db" }
+serde = { version = "1.0.105", features = ["derive"] }
+thiserror = "1.0.20"
diff --git a/server/lemmy_api_structs/src/comment.rs b/server/lemmy_api_structs/src/comment.rs
new file mode 100644
index 00000000..8dbefff6
--- /dev/null
+++ b/server/lemmy_api_structs/src/comment.rs
@@ -0,0 +1,77 @@
+use lemmy_db::comment_view::CommentView;
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize)]
+pub struct CreateComment {
+  pub content: String,
+  pub parent_id: Option<i32>,
+  pub post_id: i32,
+  pub form_id: Option<String>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditComment {
+  pub content: String,
+  pub edit_id: i32,
+  pub form_id: Option<String>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeleteComment {
+  pub edit_id: i32,
+  pub deleted: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RemoveComment {
+  pub edit_id: i32,
+  pub removed: bool,
+  pub reason: Option<String>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct MarkCommentAsRead {
+  pub edit_id: i32,
+  pub read: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct SaveComment {
+  pub comment_id: i32,
+  pub save: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct CommentResponse {
+  pub comment: CommentView,
+  pub recipient_ids: Vec<i32>,
+  pub form_id: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct CreateCommentLike {
+  pub comment_id: i32,
+  pub score: i16,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetComments {
+  pub type_: String,
+  pub sort: String,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub community_id: Option<i32>,
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetCommentsResponse {
+  pub comments: Vec<CommentView>,
+}
diff --git a/server/lemmy_api_structs/src/community.rs b/server/lemmy_api_structs/src/community.rs
new file mode 100644
index 00000000..8d07360e
--- /dev/null
+++ b/server/lemmy_api_structs/src/community.rs
@@ -0,0 +1,131 @@
+use lemmy_db::{
+  community_view::{CommunityFollowerView, CommunityModeratorView, CommunityView},
+  user_view::UserView,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize)]
+pub struct GetCommunity {
+  pub id: Option<i32>,
+  pub name: Option<String>,
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetCommunityResponse {
+  pub community: CommunityView,
+  pub moderators: Vec<CommunityModeratorView>,
+  pub online: usize,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct CreateCommunity {
+  pub name: String,
+  pub title: String,
+  pub description: Option<String>,
+  pub icon: Option<String>,
+  pub banner: Option<String>,
+  pub category_id: i32,
+  pub nsfw: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct CommunityResponse {
+  pub community: CommunityView,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct ListCommunities {
+  pub sort: String,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct ListCommunitiesResponse {
+  pub communities: Vec<CommunityView>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct BanFromCommunity {
+  pub community_id: i32,
+  pub user_id: i32,
+  pub ban: bool,
+  pub remove_data: Option<bool>,
+  pub reason: Option<String>,
+  pub expires: Option<i64>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct BanFromCommunityResponse {
+  pub user: UserView,
+  pub banned: bool,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct AddModToCommunity {
+  pub community_id: i32,
+  pub user_id: i32,
+  pub added: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct AddModToCommunityResponse {
+  pub moderators: Vec<CommunityModeratorView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditCommunity {
+  pub edit_id: i32,
+  pub title: String,
+  pub description: Option<String>,
+  pub icon: Option<String>,
+  pub banner: Option<String>,
+  pub category_id: i32,
+  pub nsfw: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeleteCommunity {
+  pub edit_id: i32,
+  pub deleted: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RemoveCommunity {
+  pub edit_id: i32,
+  pub removed: bool,
+  pub reason: Option<String>,
+  pub expires: Option<i64>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct FollowCommunity {
+  pub community_id: i32,
+  pub follow: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetFollowedCommunities {
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetFollowedCommunitiesResponse {
+  pub communities: Vec<CommunityFollowerView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct TransferCommunity {
+  pub community_id: i32,
+  pub user_id: i32,
+  pub auth: String,
+}
diff --git a/server/lemmy_api_structs/src/lib.rs b/server/lemmy_api_structs/src/lib.rs
new file mode 100644
index 00000000..df583040
--- /dev/null
+++ b/server/lemmy_api_structs/src/lib.rs
@@ -0,0 +1,24 @@
+pub extern crate serde;
+pub extern crate thiserror;
+
+pub mod comment;
+pub mod community;
+pub mod post;
+pub mod site;
+pub mod user;
+
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+#[error("{{\"error\":\"{message}\"}}")]
+pub struct APIError {
+  pub message: String,
+}
+
+impl APIError {
+  pub fn err(msg: &str) -> Self {
+    APIError {
+      message: msg.to_string(),
+    }
+  }
+}
diff --git a/server/lemmy_api_structs/src/post.rs b/server/lemmy_api_structs/src/post.rs
new file mode 100644
index 00000000..5860b2b2
--- /dev/null
+++ b/server/lemmy_api_structs/src/post.rs
@@ -0,0 +1,105 @@
+use lemmy_db::{
+  comment_view::CommentView,
+  community_view::{CommunityModeratorView, CommunityView},
+  post_view::PostView,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct CreatePost {
+  pub name: String,
+  pub url: Option<String>,
+  pub body: Option<String>,
+  pub nsfw: bool,
+  pub community_id: i32,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PostResponse {
+  pub post: PostView,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetPost {
+  pub id: i32,
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetPostResponse {
+  pub post: PostView,
+  pub comments: Vec<CommentView>,
+  pub community: CommunityView,
+  pub moderators: Vec<CommunityModeratorView>,
+  pub online: usize,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct GetPosts {
+  pub type_: String,
+  pub sort: String,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub community_id: Option<i32>,
+  pub community_name: Option<String>,
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct GetPostsResponse {
+  pub posts: Vec<PostView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct CreatePostLike {
+  pub post_id: i32,
+  pub score: i16,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditPost {
+  pub edit_id: i32,
+  pub name: String,
+  pub url: Option<String>,
+  pub body: Option<String>,
+  pub nsfw: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeletePost {
+  pub edit_id: i32,
+  pub deleted: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RemovePost {
+  pub edit_id: i32,
+  pub removed: bool,
+  pub reason: Option<String>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct LockPost {
+  pub edit_id: i32,
+  pub locked: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct StickyPost {
+  pub edit_id: i32,
+  pub stickied: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct SavePost {
+  pub post_id: i32,
+  pub save: bool,
+  pub auth: String,
+}
diff --git a/server/lemmy_api_structs/src/site.rs b/server/lemmy_api_structs/src/site.rs
new file mode 100644
index 00000000..7766a5f9
--- /dev/null
+++ b/server/lemmy_api_structs/src/site.rs
@@ -0,0 +1,127 @@
+use lemmy_db::{
+  category::*,
+  comment_view::*,
+  community_view::*,
+  moderator_views::*,
+  post_view::*,
+  site_view::*,
+  user::*,
+  user_view::*,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize)]
+pub struct ListCategories {}
+
+#[derive(Serialize, Deserialize)]
+pub struct ListCategoriesResponse {
+  pub categories: Vec<Category>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Search {
+  pub q: String,
+  pub type_: String,
+  pub community_id: Option<i32>,
+  pub sort: String,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct SearchResponse {
+  pub type_: String,
+  pub comments: Vec<CommentView>,
+  pub posts: Vec<PostView>,
+  pub communities: Vec<CommunityView>,
+  pub users: Vec<UserView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetModlog {
+  pub mod_user_id: Option<i32>,
+  pub community_id: Option<i32>,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetModlogResponse {
+  pub removed_posts: Vec<ModRemovePostView>,
+  pub locked_posts: Vec<ModLockPostView>,
+  pub stickied_posts: Vec<ModStickyPostView>,
+  pub removed_comments: Vec<ModRemoveCommentView>,
+  pub removed_communities: Vec<ModRemoveCommunityView>,
+  pub banned_from_community: Vec<ModBanFromCommunityView>,
+  pub banned: Vec<ModBanView>,
+  pub added_to_community: Vec<ModAddCommunityView>,
+  pub added: Vec<ModAddView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct CreateSite {
+  pub name: String,
+  pub description: Option<String>,
+  pub icon: Option<String>,
+  pub banner: Option<String>,
+  pub enable_downvotes: bool,
+  pub open_registration: bool,
+  pub enable_nsfw: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditSite {
+  pub name: String,
+  pub description: Option<String>,
+  pub icon: Option<String>,
+  pub banner: Option<String>,
+  pub enable_downvotes: bool,
+  pub open_registration: bool,
+  pub enable_nsfw: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetSite {
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct SiteResponse {
+  pub site: SiteView,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetSiteResponse {
+  pub site: Option<SiteView>,
+  pub admins: Vec<UserView>,
+  pub banned: Vec<UserView>,
+  pub online: usize,
+  pub version: String,
+  pub my_user: Option<User_>,
+  pub federated_instances: Vec<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct TransferSite {
+  pub user_id: i32,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetSiteConfig {
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetSiteConfigResponse {
+  pub config_hjson: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct SaveSiteConfig {
+  pub config_hjson: String,
+  pub auth: String,
+}
diff --git a/server/lemmy_api_structs/src/user.rs b/server/lemmy_api_structs/src/user.rs
new file mode 100644
index 00000000..5954e2b9
--- /dev/null
+++ b/server/lemmy_api_structs/src/user.rs
@@ -0,0 +1,239 @@
+use lemmy_db::{
+  comment_view::{CommentView, ReplyView},
+  community_view::{CommunityFollowerView, CommunityModeratorView},
+  post_view::PostView,
+  private_message_view::PrivateMessageView,
+  user_mention_view::UserMentionView,
+  user_view::UserView,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Login {
+  pub username_or_email: String,
+  pub password: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Register {
+  pub username: String,
+  pub email: Option<String>,
+  pub password: String,
+  pub password_verify: String,
+  pub admin: bool,
+  pub show_nsfw: bool,
+  pub captcha_uuid: Option<String>,
+  pub captcha_answer: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetCaptcha {}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetCaptchaResponse {
+  pub ok: Option<CaptchaResponse>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct CaptchaResponse {
+  pub png: String,         // A Base64 encoded png
+  pub wav: Option<String>, // A Base64 encoded wav audio
+  pub uuid: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct SaveUserSettings {
+  pub show_nsfw: bool,
+  pub theme: String,
+  pub default_sort_type: i16,
+  pub default_listing_type: i16,
+  pub lang: String,
+  pub avatar: Option<String>,
+  pub banner: Option<String>,
+  pub preferred_username: Option<String>,
+  pub email: Option<String>,
+  pub bio: Option<String>,
+  pub matrix_user_id: Option<String>,
+  pub new_password: Option<String>,
+  pub new_password_verify: Option<String>,
+  pub old_password: Option<String>,
+  pub show_avatars: bool,
+  pub send_notifications_to_email: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct LoginResponse {
+  pub jwt: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetUserDetails {
+  pub user_id: Option<i32>,
+  pub username: Option<String>,
+  pub sort: String,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub community_id: Option<i32>,
+  pub saved_only: bool,
+  pub auth: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetUserDetailsResponse {
+  pub user: UserView,
+  pub follows: Vec<CommunityFollowerView>,
+  pub moderates: Vec<CommunityModeratorView>,
+  pub comments: Vec<CommentView>,
+  pub posts: Vec<PostView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetRepliesResponse {
+  pub replies: Vec<ReplyView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetUserMentionsResponse {
+  pub mentions: Vec<UserMentionView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct MarkAllAsRead {
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct AddAdmin {
+  pub user_id: i32,
+  pub added: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct AddAdminResponse {
+  pub admins: Vec<UserView>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct BanUser {
+  pub user_id: i32,
+  pub ban: bool,
+  pub remove_data: Option<bool>,
+  pub reason: Option<String>,
+  pub expires: Option<i64>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct BanUserResponse {
+  pub user: UserView,
+  pub banned: bool,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetReplies {
+  pub sort: String,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub unread_only: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetUserMentions {
+  pub sort: String,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub unread_only: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct MarkUserMentionAsRead {
+  pub user_mention_id: i32,
+  pub read: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct UserMentionResponse {
+  pub mention: UserMentionView,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeleteAccount {
+  pub password: String,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct PasswordReset {
+  pub email: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PasswordResetResponse {}
+
+#[derive(Serialize, Deserialize)]
+pub struct PasswordChange {
+  pub token: String,
+  pub password: String,
+  pub password_verify: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct CreatePrivateMessage {
+  pub content: String,
+  pub recipient_id: i32,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditPrivateMessage {
+  pub edit_id: i32,
+  pub content: String,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DeletePrivateMessage {
+  pub edit_id: i32,
+  pub deleted: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct MarkPrivateMessageAsRead {
+  pub edit_id: i32,
+  pub read: bool,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetPrivateMessages {
+  pub unread_only: bool,
+  pub page: Option<i64>,
+  pub limit: Option<i64>,
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PrivateMessagesResponse {
+  pub messages: Vec<PrivateMessageView>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PrivateMessageResponse {
+  pub message: PrivateMessageView,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct UserJoin {
+  pub auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct UserJoinResponse {
+  pub user_id: i32,
+}
diff --git a/server/lemmy_rate_limit/Cargo.toml b/server/lemmy_rate_limit/Cargo.toml
new file mode 100644
index 00000000..4fc65254
--- /dev/null
+++ b/server/lemmy_rate_limit/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "lemmy_rate_limit"
+version = "0.1.0"
+authors = ["Felix Ableitner <me@nutomic.com>"]
+edition = "2018"
+
+[dependencies]
+lemmy_utils = { path = "../lemmy_utils" }
+lemmy_api_structs = { path = "../lemmy_api_structs" }
+tokio = "0.2.21"
+strum = "0.18.0"
+strum_macros = "0.18.0"
+futures = "0.3.5"
+actix-web = { version = "3.0.0-alpha.3", features = ["rustls"] }
+log = "0.4.0"
diff --git a/server/src/rate_limit/mod.rs b/server/lemmy_rate_limit/src/lib.rs
similarity index 96%
rename from server/src/rate_limit/mod.rs
rename to server/lemmy_rate_limit/src/lib.rs
index 39df7265..431a2d76 100644
--- a/server/src/rate_limit/mod.rs
+++ b/server/lemmy_rate_limit/src/lib.rs
@@ -1,5 +1,10 @@
-use super::IPAddr;
-use crate::{get_ip, LemmyError};
+#[macro_use]
+pub extern crate strum_macros;
+pub extern crate tokio;
+pub extern crate futures;
+pub extern crate actix_web;
+pub extern crate log;
+
 use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
 use futures::future::{ok, Ready};
 use lemmy_utils::settings::{RateLimitConfig, Settings};
@@ -11,6 +16,8 @@ use std::{
   task::{Context, Poll},
 };
 use tokio::sync::Mutex;
+use lemmy_utils::get_ip;
+use lemmy_utils::LemmyError;
 
 pub mod rate_limiter;
 
diff --git a/server/src/rate_limit/rate_limiter.rs b/server/lemmy_rate_limit/src/rate_limiter.rs
similarity index 94%
rename from server/src/rate_limit/rate_limiter.rs
rename to server/lemmy_rate_limit/src/rate_limiter.rs
index f1a38841..1c90f90e 100644
--- a/server/src/rate_limit/rate_limiter.rs
+++ b/server/lemmy_rate_limit/src/rate_limiter.rs
@@ -1,5 +1,6 @@
-use super::IPAddr;
-use crate::{api::APIError, LemmyError};
+use lemmy_utils::IPAddr;
+use lemmy_utils::LemmyError;
+use lemmy_api_structs::APIError;
 use log::debug;
 use std::{collections::HashMap, time::SystemTime};
 use strum::IntoEnumIterator;
@@ -27,7 +28,7 @@ pub struct RateLimiter {
 impl Default for RateLimiter {
   fn default() -> Self {
     Self {
-      buckets: HashMap::new(),
+      buckets: HashMap::<RateLimitType, HashMap<IPAddr, RateLimitBucket>>::new(),
     }
   }
 }
diff --git a/server/lemmy_utils/Cargo.toml b/server/lemmy_utils/Cargo.toml
index 9685c0ed..c6ab744b 100644
--- a/server/lemmy_utils/Cargo.toml
+++ b/server/lemmy_utils/Cargo.toml
@@ -20,3 +20,5 @@ comrak = "0.7"
 lazy_static = "1.3.0"
 openssl = "0.10"
 url = { version = "2.1.1", features = ["serde"] }
+actix-web = "3.0.0-alpha.3"
+anyhow = "1.0.32"
diff --git a/server/lemmy_utils/src/lib.rs b/server/lemmy_utils/src/lib.rs
index 41cdcec8..97f32f27 100644
--- a/server/lemmy_utils/src/lib.rs
+++ b/server/lemmy_utils/src/lib.rs
@@ -8,6 +8,8 @@ pub extern crate rand;
 pub extern crate regex;
 pub extern crate serde_json;
 pub extern crate url;
+pub extern crate actix_web;
+pub extern crate anyhow;
 
 pub mod settings;
 
@@ -30,6 +32,13 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng};
 use regex::{Regex, RegexBuilder};
 use std::io::{Error, ErrorKind};
 use url::Url;
+use actix_web::dev::ConnectionInfo;
+
+pub type ConnectionId = usize;
+pub type PostId = i32;
+pub type CommunityId = i32;
+pub type UserId = i32;
+pub type IPAddr = String;
 
 #[macro_export]
 macro_rules! location_info {
@@ -43,6 +52,28 @@ macro_rules! location_info {
   };
 }
 
+#[derive(Debug)]
+pub struct LemmyError {
+  inner: anyhow::Error,
+}
+
+impl<T> From<T> for LemmyError
+  where
+    T: Into<anyhow::Error>,
+{
+  fn from(t: T) -> Self {
+    LemmyError { inner: t.into() }
+  }
+}
+
+impl std::fmt::Display for LemmyError {
+  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+    self.inner.fmt(f)
+  }
+}
+
+impl actix_web::error::ResponseError for LemmyError {}
+
 pub fn naive_from_unix(time: i64) -> NaiveDateTime {
   NaiveDateTime::from_timestamp(time, 0)
 }
@@ -346,3 +377,13 @@ pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url {
   ))
   .unwrap()
 }
+
+pub fn get_ip(conn_info: &ConnectionInfo) -> String {
+  conn_info
+    .realip_remote_addr()
+    .unwrap_or("127.0.0.1:12345")
+    .split(':')
+    .next()
+    .unwrap_or("127.0.0.1")
+    .to_string()
+}
\ No newline at end of file
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs
index 0d250a7b..9ad52108 100644
--- a/server/src/api/comment.rs
+++ b/server/src/api/comment.rs
@@ -5,7 +5,6 @@ use crate::{
     get_user_from_jwt,
     get_user_from_jwt_opt,
     is_mod_or_admin,
-    APIError,
     Perform,
   },
   apub::{ApubLikeableType, ApubObjectType},
@@ -14,12 +13,11 @@ use crate::{
     messages::{JoinCommunityRoom, SendComment},
     UserOperation,
   },
-  ConnectionId,
   DbPool,
   LemmyContext,
-  LemmyError,
 };
 use actix_web::web::Data;
+use lemmy_api_structs::{comment::*, APIError};
 use lemmy_db::{
   comment::*,
   comment_view::*,
@@ -40,88 +38,14 @@ use lemmy_utils::{
   scrape_text_for_mentions,
   send_email,
   settings::Settings,
+  ConnectionId,
   EndpointType,
+  LemmyError,
   MentionData,
 };
 use log::error;
-use serde::{Deserialize, Serialize};
 use std::str::FromStr;
 
-#[derive(Serialize, Deserialize)]
-pub struct CreateComment {
-  content: String,
-  parent_id: Option<i32>,
-  pub post_id: i32,
-  form_id: Option<String>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct EditComment {
-  content: String,
-  edit_id: i32,
-  form_id: Option<String>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct DeleteComment {
-  edit_id: i32,
-  deleted: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct RemoveComment {
-  edit_id: i32,
-  removed: bool,
-  reason: Option<String>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct MarkCommentAsRead {
-  edit_id: i32,
-  read: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct SaveComment {
-  comment_id: i32,
-  save: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct CommentResponse {
-  pub comment: CommentView,
-  pub recipient_ids: Vec<i32>,
-  pub form_id: Option<String>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct CreateCommentLike {
-  comment_id: i32,
-  score: i16,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetComments {
-  type_: String,
-  sort: String,
-  page: Option<i64>,
-  limit: Option<i64>,
-  pub community_id: Option<i32>,
-  auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetCommentsResponse {
-  comments: Vec<CommentView>,
-}
-
 #[async_trait::async_trait(?Send)]
 impl Perform for CreateComment {
   type Response = CommentResponse;
diff --git a/server/src/api/community.rs b/server/src/api/community.rs
index 763a9114..17eb1c5c 100644
--- a/server/src/api/community.rs
+++ b/server/src/api/community.rs
@@ -6,7 +6,6 @@ use crate::{
     get_user_from_jwt_opt,
     is_admin,
     is_mod_or_admin,
-    APIError,
     Perform,
   },
   apub::ActorType,
@@ -15,12 +14,11 @@ use crate::{
     messages::{GetCommunityUsersOnline, JoinCommunityRoom, SendCommunityRoomMessage},
     UserOperation,
   },
-  ConnectionId,
   LemmyContext,
-  LemmyError,
 };
 use actix_web::web::Data;
 use anyhow::Context;
+use lemmy_api_structs::{community::*, APIError};
 use lemmy_db::{
   comment::Comment,
   comment_view::CommentQueryBuilder,
@@ -44,137 +42,12 @@ use lemmy_utils::{
   location_info,
   make_apub_endpoint,
   naive_from_unix,
+  ConnectionId,
   EndpointType,
+  LemmyError,
 };
-use serde::{Deserialize, Serialize};
 use std::str::FromStr;
 
-#[derive(Serialize, Deserialize)]
-pub struct GetCommunity {
-  id: Option<i32>,
-  pub name: Option<String>,
-  auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetCommunityResponse {
-  pub community: CommunityView,
-  pub moderators: Vec<CommunityModeratorView>,
-  pub online: usize,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct CreateCommunity {
-  name: String,
-  title: String,
-  description: Option<String>,
-  icon: Option<String>,
-  banner: Option<String>,
-  category_id: i32,
-  nsfw: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct CommunityResponse {
-  pub community: CommunityView,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ListCommunities {
-  pub sort: String,
-  pub page: Option<i64>,
-  pub limit: Option<i64>,
-  pub auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct ListCommunitiesResponse {
-  pub communities: Vec<CommunityView>,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct BanFromCommunity {
-  pub community_id: i32,
-  user_id: i32,
-  ban: bool,
-  remove_data: Option<bool>,
-  reason: Option<String>,
-  expires: Option<i64>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct BanFromCommunityResponse {
-  user: UserView,
-  banned: bool,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct AddModToCommunity {
-  pub community_id: i32,
-  user_id: i32,
-  added: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct AddModToCommunityResponse {
-  moderators: Vec<CommunityModeratorView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct EditCommunity {
-  pub edit_id: i32,
-  title: String,
-  description: Option<String>,
-  icon: Option<String>,
-  banner: Option<String>,
-  category_id: i32,
-  nsfw: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct DeleteCommunity {
-  pub edit_id: i32,
-  deleted: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct RemoveCommunity {
-  pub edit_id: i32,
-  removed: bool,
-  reason: Option<String>,
-  expires: Option<i64>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct FollowCommunity {
-  community_id: i32,
-  follow: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetFollowedCommunities {
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetFollowedCommunitiesResponse {
-  communities: Vec<CommunityFollowerView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct TransferCommunity {
-  community_id: i32,
-  user_id: i32,
-  auth: String,
-}
-
 #[async_trait::async_trait(?Send)]
 impl Perform for GetCommunity {
   type Response = GetCommunityResponse;
diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs
index 1b213d9f..03a585f7 100644
--- a/server/src/api/mod.rs
+++ b/server/src/api/mod.rs
@@ -1,5 +1,6 @@
-use crate::{api::claims::Claims, blocking, ConnectionId, DbPool, LemmyContext, LemmyError};
+use crate::{api::claims::Claims, blocking, DbPool, LemmyContext};
 use actix_web::web::Data;
+use lemmy_api_structs::APIError;
 use lemmy_db::{
   community::Community,
   community_view::CommunityUserBanView,
@@ -7,8 +8,7 @@ use lemmy_db::{
   user::User_,
   Crud,
 };
-use lemmy_utils::{slur_check, slurs_vec_to_str};
-use thiserror::Error;
+use lemmy_utils::{slur_check, slurs_vec_to_str, ConnectionId, LemmyError};
 
 pub mod claims;
 pub mod comment;
@@ -17,20 +17,6 @@ pub mod post;
 pub mod site;
 pub mod user;
 
-#[derive(Debug, Error)]
-#[error("{{\"error\":\"{message}\"}}")]
-pub struct APIError {
-  pub message: String,
-}
-
-impl APIError {
-  pub fn err(msg: &str) -> Self {
-    APIError {
-      message: msg.to_string(),
-    }
-  }
-}
-
 #[async_trait::async_trait(?Send)]
 pub trait Perform {
   type Response: serde::ser::Serialize + Send;
diff --git a/server/src/api/post.rs b/server/src/api/post.rs
index 84842173..0eeaae43 100644
--- a/server/src/api/post.rs
+++ b/server/src/api/post.rs
@@ -6,7 +6,6 @@ use crate::{
     get_user_from_jwt,
     get_user_from_jwt_opt,
     is_mod_or_admin,
-    APIError,
     Perform,
   },
   apub::{ApubLikeableType, ApubObjectType},
@@ -16,11 +15,10 @@ use crate::{
     messages::{GetPostUsersOnline, JoinCommunityRoom, JoinPostRoom, SendPost},
     UserOperation,
   },
-  ConnectionId,
   LemmyContext,
-  LemmyError,
 };
 use actix_web::web::Data;
+use lemmy_api_structs::{post::*, APIError};
 use lemmy_db::{
   comment_view::*,
   community_view::*,
@@ -35,110 +33,16 @@ use lemmy_db::{
   Saveable,
   SortType,
 };
-use lemmy_utils::{is_valid_post_title, make_apub_endpoint, EndpointType};
-use serde::{Deserialize, Serialize};
+use lemmy_utils::{
+  is_valid_post_title,
+  make_apub_endpoint,
+  ConnectionId,
+  EndpointType,
+  LemmyError,
+};
 use std::str::FromStr;
 use url::Url;
 
-#[derive(Serialize, Deserialize, Debug)]
-pub struct CreatePost {
-  name: String,
-  url: Option<String>,
-  body: Option<String>,
-  nsfw: bool,
-  pub community_id: i32,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct PostResponse {
-  pub post: PostView,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetPost {
-  pub id: i32,
-  auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetPostResponse {
-  post: PostView,
-  comments: Vec<CommentView>,
-  community: CommunityView,
-  moderators: Vec<CommunityModeratorView>,
-  pub online: usize,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct GetPosts {
-  type_: String,
-  sort: String,
-  page: Option<i64>,
-  limit: Option<i64>,
-  pub community_id: Option<i32>,
-  pub community_name: Option<String>,
-  auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct GetPostsResponse {
-  pub posts: Vec<PostView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct CreatePostLike {
-  post_id: i32,
-  score: i16,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct EditPost {
-  pub edit_id: i32,
-  name: String,
-  url: Option<String>,
-  body: Option<String>,
-  nsfw: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct DeletePost {
-  pub edit_id: i32,
-  deleted: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct RemovePost {
-  pub edit_id: i32,
-  removed: bool,
-  reason: Option<String>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct LockPost {
-  pub edit_id: i32,
-  locked: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct StickyPost {
-  pub edit_id: i32,
-  stickied: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct SavePost {
-  post_id: i32,
-  save: bool,
-  auth: String,
-}
-
 #[async_trait::async_trait(?Send)]
 impl Perform for CreatePost {
   type Response = PostResponse;
diff --git a/server/src/api/site.rs b/server/src/api/site.rs
index 2ea4a390..abc4161e 100644
--- a/server/src/api/site.rs
+++ b/server/src/api/site.rs
@@ -1,4 +1,3 @@
-use super::user::Register;
 use crate::{
   api::{
     check_slurs,
@@ -6,7 +5,6 @@ use crate::{
     get_user_from_jwt,
     get_user_from_jwt_opt,
     is_admin,
-    APIError,
     Perform,
   },
   apub::fetcher::search_by_apub_id,
@@ -16,12 +14,11 @@ use crate::{
     messages::{GetUsersOnline, SendAllMessage},
     UserOperation,
   },
-  ConnectionId,
   LemmyContext,
-  LemmyError,
 };
 use actix_web::web::Data;
 use anyhow::Context;
+use lemmy_api_structs::{site::*, user::Register, APIError};
 use lemmy_db::{
   category::*,
   comment_view::*,
@@ -33,133 +30,15 @@ use lemmy_db::{
   post_view::*,
   site::*,
   site_view::*,
-  user::*,
   user_view::*,
   Crud,
   SearchType,
   SortType,
 };
-use lemmy_utils::{location_info, settings::Settings};
+use lemmy_utils::{location_info, settings::Settings, ConnectionId, LemmyError};
 use log::{debug, info};
-use serde::{Deserialize, Serialize};
 use std::str::FromStr;
 
-#[derive(Serialize, Deserialize)]
-pub struct ListCategories {}
-
-#[derive(Serialize, Deserialize)]
-pub struct ListCategoriesResponse {
-  categories: Vec<Category>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct Search {
-  q: String,
-  type_: String,
-  community_id: Option<i32>,
-  sort: String,
-  page: Option<i64>,
-  limit: Option<i64>,
-  auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct SearchResponse {
-  pub type_: String,
-  pub comments: Vec<CommentView>,
-  pub posts: Vec<PostView>,
-  pub communities: Vec<CommunityView>,
-  pub users: Vec<UserView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetModlog {
-  mod_user_id: Option<i32>,
-  community_id: Option<i32>,
-  page: Option<i64>,
-  limit: Option<i64>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetModlogResponse {
-  removed_posts: Vec<ModRemovePostView>,
-  locked_posts: Vec<ModLockPostView>,
-  stickied_posts: Vec<ModStickyPostView>,
-  removed_comments: Vec<ModRemoveCommentView>,
-  removed_communities: Vec<ModRemoveCommunityView>,
-  banned_from_community: Vec<ModBanFromCommunityView>,
-  banned: Vec<ModBanView>,
-  added_to_community: Vec<ModAddCommunityView>,
-  added: Vec<ModAddView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct CreateSite {
-  pub name: String,
-  pub description: Option<String>,
-  pub icon: Option<String>,
-  pub banner: Option<String>,
-  pub enable_downvotes: bool,
-  pub open_registration: bool,
-  pub enable_nsfw: bool,
-  pub auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct EditSite {
-  name: String,
-  description: Option<String>,
-  icon: Option<String>,
-  banner: Option<String>,
-  enable_downvotes: bool,
-  open_registration: bool,
-  enable_nsfw: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetSite {
-  auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct SiteResponse {
-  site: SiteView,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetSiteResponse {
-  site: Option<SiteView>,
-  admins: Vec<UserView>,
-  banned: Vec<UserView>,
-  pub online: usize,
-  version: String,
-  my_user: Option<User_>,
-  federated_instances: Vec<String>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct TransferSite {
-  user_id: i32,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetSiteConfig {
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetSiteConfigResponse {
-  config_hjson: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct SaveSiteConfig {
-  config_hjson: String,
-  auth: String,
-}
-
 #[async_trait::async_trait(?Send)]
 impl Perform for ListCategories {
   type Response = ListCategoriesResponse;
diff --git a/server/src/api/user.rs b/server/src/api/user.rs
index 32a9d2b8..a40f296e 100644
--- a/server/src/api/user.rs
+++ b/server/src/api/user.rs
@@ -1,13 +1,5 @@
 use crate::{
-  api::{
-    check_slurs,
-    claims::Claims,
-    get_user_from_jwt,
-    get_user_from_jwt_opt,
-    is_admin,
-    APIError,
-    Perform,
-  },
+  api::{check_slurs, claims::Claims, get_user_from_jwt, get_user_from_jwt_opt, is_admin, Perform},
   apub::ApubObjectType,
   blocking,
   captcha_espeak_wav_base64,
@@ -15,15 +7,14 @@ use crate::{
     messages::{CaptchaItem, CheckCaptcha, JoinUserRoom, SendAllMessage, SendUserRoomMessage},
     UserOperation,
   },
-  ConnectionId,
   LemmyContext,
-  LemmyError,
 };
 use actix_web::web::Data;
 use anyhow::Context;
 use bcrypt::verify;
 use captcha::{gen, Difficulty};
 use chrono::Duration;
+use lemmy_api_structs::{user::*, APIError};
 use lemmy_db::{
   comment::*,
   comment_view::*,
@@ -60,242 +51,13 @@ use lemmy_utils::{
   remove_slurs,
   send_email,
   settings::Settings,
+  ConnectionId,
   EndpointType,
+  LemmyError,
 };
 use log::error;
-use serde::{Deserialize, Serialize};
 use std::str::FromStr;
 
-#[derive(Serialize, Deserialize, Debug)]
-pub struct Login {
-  username_or_email: String,
-  password: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct Register {
-  pub username: String,
-  pub email: Option<String>,
-  pub password: String,
-  pub password_verify: String,
-  pub admin: bool,
-  pub show_nsfw: bool,
-  pub captcha_uuid: Option<String>,
-  pub captcha_answer: Option<String>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetCaptcha {}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetCaptchaResponse {
-  ok: Option<CaptchaResponse>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct CaptchaResponse {
-  png: String,         // A Base64 encoded png
-  wav: Option<String>, // A Base64 encoded wav audio
-  uuid: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct SaveUserSettings {
-  show_nsfw: bool,
-  theme: String,
-  default_sort_type: i16,
-  default_listing_type: i16,
-  lang: String,
-  avatar: Option<String>,
-  banner: Option<String>,
-  preferred_username: Option<String>,
-  email: Option<String>,
-  bio: Option<String>,
-  matrix_user_id: Option<String>,
-  new_password: Option<String>,
-  new_password_verify: Option<String>,
-  old_password: Option<String>,
-  show_avatars: bool,
-  send_notifications_to_email: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct LoginResponse {
-  pub jwt: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetUserDetails {
-  user_id: Option<i32>,
-  username: Option<String>,
-  sort: String,
-  page: Option<i64>,
-  limit: Option<i64>,
-  community_id: Option<i32>,
-  saved_only: bool,
-  auth: Option<String>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetUserDetailsResponse {
-  user: UserView,
-  follows: Vec<CommunityFollowerView>,
-  moderates: Vec<CommunityModeratorView>,
-  comments: Vec<CommentView>,
-  posts: Vec<PostView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetRepliesResponse {
-  replies: Vec<ReplyView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetUserMentionsResponse {
-  mentions: Vec<UserMentionView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct MarkAllAsRead {
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct AddAdmin {
-  user_id: i32,
-  added: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct AddAdminResponse {
-  admins: Vec<UserView>,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct BanUser {
-  user_id: i32,
-  ban: bool,
-  remove_data: Option<bool>,
-  reason: Option<String>,
-  expires: Option<i64>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct BanUserResponse {
-  user: UserView,
-  banned: bool,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetReplies {
-  sort: String,
-  page: Option<i64>,
-  limit: Option<i64>,
-  unread_only: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetUserMentions {
-  sort: String,
-  page: Option<i64>,
-  limit: Option<i64>,
-  unread_only: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct MarkUserMentionAsRead {
-  user_mention_id: i32,
-  read: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct UserMentionResponse {
-  mention: UserMentionView,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct DeleteAccount {
-  password: String,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct PasswordReset {
-  email: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct PasswordResetResponse {}
-
-#[derive(Serialize, Deserialize)]
-pub struct PasswordChange {
-  token: String,
-  password: String,
-  password_verify: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct CreatePrivateMessage {
-  content: String,
-  pub recipient_id: i32,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct EditPrivateMessage {
-  edit_id: i32,
-  content: String,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct DeletePrivateMessage {
-  edit_id: i32,
-  deleted: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct MarkPrivateMessageAsRead {
-  edit_id: i32,
-  read: bool,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct GetPrivateMessages {
-  unread_only: bool,
-  page: Option<i64>,
-  limit: Option<i64>,
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct PrivateMessagesResponse {
-  messages: Vec<PrivateMessageView>,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct PrivateMessageResponse {
-  pub message: PrivateMessageView,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct UserJoin {
-  auth: String,
-}
-
-#[derive(Serialize, Deserialize, Clone)]
-pub struct UserJoinResponse {
-  pub user_id: i32,
-}
-
 #[async_trait::async_trait(?Send)]
 impl Perform for Login {
   type Response = LoginResponse;
diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs
index b4d6c4d2..0ba8f873 100644
--- a/server/src/apub/activities.rs
+++ b/server/src/apub/activities.rs
@@ -1,14 +1,13 @@
 use crate::{
   apub::{activity_queue::send_activity, community::do_announce, insert_activity},
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   base::{Extends, ExtendsExt},
   object::AsObject,
 };
 use lemmy_db::{community::Community, user::User_};
-use lemmy_utils::{get_apub_protocol_string, settings::Settings};
+use lemmy_utils::{get_apub_protocol_string, settings::Settings, LemmyError};
 use serde::{export::fmt::Debug, Serialize};
 use url::{ParseError, Url};
 use uuid::Uuid;
diff --git a/server/src/apub/activity_queue.rs b/server/src/apub/activity_queue.rs
index bc5faaa3..008846bf 100644
--- a/server/src/apub/activity_queue.rs
+++ b/server/src/apub/activity_queue.rs
@@ -1,7 +1,4 @@
-use crate::{
-  apub::{check_is_apub_id_valid, extensions::signatures::sign, ActorType},
-  LemmyError,
-};
+use crate::apub::{check_is_apub_id_valid, extensions::signatures::sign, ActorType};
 use activitystreams::{
   base::{Extends, ExtendsExt},
   object::AsObject,
@@ -17,7 +14,7 @@ use background_jobs::{
   QueueHandle,
   WorkerConfig,
 };
-use lemmy_utils::{location_info, settings::Settings};
+use lemmy_utils::{location_info, settings::Settings, LemmyError};
 use log::warn;
 use serde::{Deserialize, Serialize};
 use std::{future::Future, pin::Pin};
diff --git a/server/src/apub/comment.rs b/server/src/apub/comment.rs
index 988904e9..8c416369 100644
--- a/server/src/apub/comment.rs
+++ b/server/src/apub/comment.rs
@@ -20,7 +20,6 @@ use crate::{
   blocking,
   DbPool,
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{
@@ -54,6 +53,7 @@ use lemmy_utils::{
   location_info,
   remove_slurs,
   scrape_text_for_mentions,
+  LemmyError,
   MentionData,
 };
 use log::debug;
diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs
index 67baa786..8d9b4dc1 100644
--- a/server/src/apub/community.rs
+++ b/server/src/apub/community.rs
@@ -18,7 +18,6 @@ use crate::{
   blocking,
   DbPool,
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{
@@ -48,7 +47,7 @@ use lemmy_db::{
   post::Post,
   user::User_,
 };
-use lemmy_utils::{convert_datetime, get_apub_protocol_string, location_info};
+use lemmy_utils::{convert_datetime, get_apub_protocol_string, location_info, LemmyError};
 use serde::Deserialize;
 use url::Url;
 
diff --git a/server/src/apub/extensions/group_extensions.rs b/server/src/apub/extensions/group_extensions.rs
index 3099a273..766b3d79 100644
--- a/server/src/apub/extensions/group_extensions.rs
+++ b/server/src/apub/extensions/group_extensions.rs
@@ -1,8 +1,8 @@
-use crate::LemmyError;
 use activitystreams::unparsed::UnparsedMutExt;
 use activitystreams_ext::UnparsedExtension;
 use diesel::PgConnection;
 use lemmy_db::{category::Category, Crud};
+use lemmy_utils::LemmyError;
 use serde::{Deserialize, Serialize};
 
 #[derive(Clone, Debug, Default, Deserialize, Serialize)]
diff --git a/server/src/apub/extensions/signatures.rs b/server/src/apub/extensions/signatures.rs
index cb969777..4a261c17 100644
--- a/server/src/apub/extensions/signatures.rs
+++ b/server/src/apub/extensions/signatures.rs
@@ -1,4 +1,4 @@
-use crate::{apub::ActorType, LemmyError};
+use crate::apub::ActorType;
 use activitystreams::unparsed::UnparsedMutExt;
 use activitystreams_ext::UnparsedExtension;
 use actix_web::{client::ClientRequest, HttpRequest};
@@ -7,7 +7,7 @@ use http_signature_normalization_actix::{
   digest::{DigestClient, SignExt},
   Config,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 use log::debug;
 use openssl::{
   hash::MessageDigest,
diff --git a/server/src/apub/fetcher.rs b/server/src/apub/fetcher.rs
index 0fcb1150..d4516577 100644
--- a/server/src/apub/fetcher.rs
+++ b/server/src/apub/fetcher.rs
@@ -1,5 +1,4 @@
 use crate::{
-  api::site::SearchResponse,
   apub::{
     check_is_apub_id_valid,
     ActorType,
@@ -12,12 +11,12 @@ use crate::{
   blocking,
   request::{retry, RecvError},
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{base::BaseExt, collection::OrderedCollection, object::Note, prelude::*};
 use anyhow::{anyhow, Context};
 use chrono::NaiveDateTime;
 use diesel::result::Error::NotFound;
+use lemmy_api_structs::site::SearchResponse;
 use lemmy_db::{
   comment::{Comment, CommentForm},
   comment_view::CommentView,
@@ -32,7 +31,7 @@ use lemmy_db::{
   Joinable,
   SearchType,
 };
-use lemmy_utils::{get_apub_protocol_string, location_info};
+use lemmy_utils::{get_apub_protocol_string, location_info, LemmyError};
 use log::debug;
 use reqwest::Client;
 use serde::Deserialize;
diff --git a/server/src/apub/inbox/activities/announce.rs b/server/src/apub/inbox/activities/announce.rs
index e0fb8065..47607a05 100644
--- a/server/src/apub/inbox/activities/announce.rs
+++ b/server/src/apub/inbox/activities/announce.rs
@@ -12,7 +12,6 @@ use crate::{
     shared_inbox::{get_community_id_from_activity, receive_unhandled_activity},
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::*,
@@ -21,7 +20,7 @@ use activitystreams::{
 };
 use actix_web::HttpResponse;
 use anyhow::Context;
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 
 pub async fn receive_announce(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/activities/create.rs b/server/src/apub/inbox/activities/create.rs
index a7d7d575..a3915204 100644
--- a/server/src/apub/inbox/activities/create.rs
+++ b/server/src/apub/inbox/activities/create.rs
@@ -1,8 +1,5 @@
 use crate::{
-  api::{
-    comment::{send_local_notifs, CommentResponse},
-    post::PostResponse,
-  },
+  api::comment::send_local_notifs,
   apub::{
     inbox::shared_inbox::{
       announce_if_community_is_local,
@@ -19,18 +16,18 @@ use crate::{
     UserOperation,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{activity::Create, base::AnyBase, object::Note, prelude::*};
 use actix_web::HttpResponse;
 use anyhow::Context;
+use lemmy_api_structs::{comment::CommentResponse, post::PostResponse};
 use lemmy_db::{
   comment::{Comment, CommentForm},
   comment_view::CommentView,
   post::{Post, PostForm},
   post_view::PostView,
 };
-use lemmy_utils::{location_info, scrape_text_for_mentions};
+use lemmy_utils::{location_info, scrape_text_for_mentions, LemmyError};
 
 pub async fn receive_create(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/activities/delete.rs b/server/src/apub/inbox/activities/delete.rs
index 65180ca4..70bf3761 100644
--- a/server/src/apub/inbox/activities/delete.rs
+++ b/server/src/apub/inbox/activities/delete.rs
@@ -1,5 +1,4 @@
 use crate::{
-  api::{comment::CommentResponse, community::CommunityResponse, post::PostResponse},
   apub::{
     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
     inbox::shared_inbox::{
@@ -18,11 +17,15 @@ use crate::{
     UserOperation,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{activity::Delete, base::AnyBase, object::Note, prelude::*};
 use actix_web::HttpResponse;
 use anyhow::Context;
+use lemmy_api_structs::{
+  comment::CommentResponse,
+  community::CommunityResponse,
+  post::PostResponse,
+};
 use lemmy_db::{
   comment::{Comment, CommentForm},
   comment_view::CommentView,
@@ -33,7 +36,7 @@ use lemmy_db::{
   post_view::PostView,
   Crud,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 
 pub async fn receive_delete(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/activities/dislike.rs b/server/src/apub/inbox/activities/dislike.rs
index 441e36f1..599389b1 100644
--- a/server/src/apub/inbox/activities/dislike.rs
+++ b/server/src/apub/inbox/activities/dislike.rs
@@ -1,5 +1,4 @@
 use crate::{
-  api::{comment::CommentResponse, post::PostResponse},
   apub::{
     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
     inbox::shared_inbox::{
@@ -16,11 +15,11 @@ use crate::{
     UserOperation,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{activity::Dislike, base::AnyBase, object::Note, prelude::*};
 use actix_web::HttpResponse;
 use anyhow::Context;
+use lemmy_api_structs::{comment::CommentResponse, post::PostResponse};
 use lemmy_db::{
   comment::{CommentForm, CommentLike, CommentLikeForm},
   comment_view::CommentView,
@@ -28,7 +27,7 @@ use lemmy_db::{
   post_view::PostView,
   Likeable,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 
 pub async fn receive_dislike(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/activities/like.rs b/server/src/apub/inbox/activities/like.rs
index 67aefaa0..2cb95521 100644
--- a/server/src/apub/inbox/activities/like.rs
+++ b/server/src/apub/inbox/activities/like.rs
@@ -1,5 +1,4 @@
 use crate::{
-  api::{comment::CommentResponse, post::PostResponse},
   apub::{
     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
     inbox::shared_inbox::{
@@ -16,11 +15,11 @@ use crate::{
     UserOperation,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{activity::Like, base::AnyBase, object::Note, prelude::*};
 use actix_web::HttpResponse;
 use anyhow::Context;
+use lemmy_api_structs::{comment::CommentResponse, post::PostResponse};
 use lemmy_db::{
   comment::{CommentForm, CommentLike, CommentLikeForm},
   comment_view::CommentView,
@@ -28,7 +27,7 @@ use lemmy_db::{
   post_view::PostView,
   Likeable,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 
 pub async fn receive_like(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/activities/remove.rs b/server/src/apub/inbox/activities/remove.rs
index f0e98be2..846842d4 100644
--- a/server/src/apub/inbox/activities/remove.rs
+++ b/server/src/apub/inbox/activities/remove.rs
@@ -1,5 +1,4 @@
 use crate::{
-  api::{comment::CommentResponse, community::CommunityResponse, post::PostResponse},
   apub::{
     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
     inbox::shared_inbox::{
@@ -19,11 +18,15 @@ use crate::{
     UserOperation,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{activity::Remove, base::AnyBase, object::Note, prelude::*};
 use actix_web::HttpResponse;
 use anyhow::{anyhow, Context};
+use lemmy_api_structs::{
+  comment::CommentResponse,
+  community::CommunityResponse,
+  post::PostResponse,
+};
 use lemmy_db::{
   comment::{Comment, CommentForm},
   comment_view::CommentView,
@@ -34,7 +37,7 @@ use lemmy_db::{
   post_view::PostView,
   Crud,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 
 pub async fn receive_remove(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/activities/undo.rs b/server/src/apub/inbox/activities/undo.rs
index 9bf9e96a..0b695d32 100644
--- a/server/src/apub/inbox/activities/undo.rs
+++ b/server/src/apub/inbox/activities/undo.rs
@@ -1,5 +1,4 @@
 use crate::{
-  api::{comment::CommentResponse, community::CommunityResponse, post::PostResponse},
   apub::{
     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
     inbox::shared_inbox::{
@@ -18,7 +17,6 @@ use crate::{
     UserOperation,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::*,
@@ -28,6 +26,11 @@ use activitystreams::{
 };
 use actix_web::HttpResponse;
 use anyhow::{anyhow, Context};
+use lemmy_api_structs::{
+  comment::CommentResponse,
+  community::CommunityResponse,
+  post::PostResponse,
+};
 use lemmy_db::{
   comment::{Comment, CommentForm, CommentLike},
   comment_view::CommentView,
@@ -39,7 +42,7 @@ use lemmy_db::{
   Crud,
   Likeable,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 
 pub async fn receive_undo(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/activities/update.rs b/server/src/apub/inbox/activities/update.rs
index d5d01235..eb1b67f1 100644
--- a/server/src/apub/inbox/activities/update.rs
+++ b/server/src/apub/inbox/activities/update.rs
@@ -1,8 +1,5 @@
 use crate::{
-  api::{
-    comment::{send_local_notifs, CommentResponse},
-    post::PostResponse,
-  },
+  api::comment::send_local_notifs,
   apub::{
     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
     inbox::shared_inbox::{
@@ -20,11 +17,11 @@ use crate::{
     UserOperation,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{activity::Update, base::AnyBase, object::Note, prelude::*};
 use actix_web::HttpResponse;
 use anyhow::Context;
+use lemmy_api_structs::{comment::CommentResponse, post::PostResponse};
 use lemmy_db::{
   comment::{Comment, CommentForm},
   comment_view::CommentView,
@@ -32,7 +29,7 @@ use lemmy_db::{
   post_view::PostView,
   Crud,
 };
-use lemmy_utils::{location_info, scrape_text_for_mentions};
+use lemmy_utils::{location_info, scrape_text_for_mentions, LemmyError};
 
 pub async fn receive_update(
   activity: AnyBase,
diff --git a/server/src/apub/inbox/community_inbox.rs b/server/src/apub/inbox/community_inbox.rs
index 929b8511..0631f939 100644
--- a/server/src/apub/inbox/community_inbox.rs
+++ b/server/src/apub/inbox/community_inbox.rs
@@ -8,7 +8,6 @@ use crate::{
   },
   blocking,
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{ActorAndObject, Follow, Undo},
@@ -22,7 +21,7 @@ use lemmy_db::{
   user::User_,
   Followable,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 use log::debug;
 use serde::{Deserialize, Serialize};
 use std::fmt::Debug;
diff --git a/server/src/apub/inbox/shared_inbox.rs b/server/src/apub/inbox/shared_inbox.rs
index 0f8cc8ed..da795108 100644
--- a/server/src/apub/inbox/shared_inbox.rs
+++ b/server/src/apub/inbox/shared_inbox.rs
@@ -21,7 +21,6 @@ use crate::{
     insert_activity,
   },
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{ActorAndObject, ActorAndObjectRef},
@@ -32,7 +31,7 @@ use activitystreams::{
 use actix_web::{web, HttpRequest, HttpResponse};
 use anyhow::Context;
 use lemmy_db::user::User_;
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 use log::debug;
 use serde::{Deserialize, Serialize};
 use std::fmt::Debug;
diff --git a/server/src/apub/inbox/user_inbox.rs b/server/src/apub/inbox/user_inbox.rs
index ddb97109..7ea95833 100644
--- a/server/src/apub/inbox/user_inbox.rs
+++ b/server/src/apub/inbox/user_inbox.rs
@@ -1,5 +1,4 @@
 use crate::{
-  api::user::PrivateMessageResponse,
   apub::{
     check_is_apub_id_valid,
     extensions::signatures::verify,
@@ -10,7 +9,6 @@ use crate::{
   blocking,
   websocket::{messages::SendUserRoomMessage, UserOperation},
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{Accept, ActorAndObject, Create, Delete, Undo, Update},
@@ -20,6 +18,7 @@ use activitystreams::{
 };
 use actix_web::{web, HttpRequest, HttpResponse};
 use anyhow::Context;
+use lemmy_api_structs::user::PrivateMessageResponse;
 use lemmy_db::{
   community::{CommunityFollower, CommunityFollowerForm},
   naive_now,
@@ -29,7 +28,7 @@ use lemmy_db::{
   Crud,
   Followable,
 };
-use lemmy_utils::location_info;
+use lemmy_utils::{location_info, LemmyError};
 use log::debug;
 use serde::{Deserialize, Serialize};
 use std::fmt::Debug;
diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs
index c545a5fd..b3b161c7 100644
--- a/server/src/apub/mod.rs
+++ b/server/src/apub/mod.rs
@@ -20,7 +20,6 @@ use crate::{
   routes::webfinger::WebFingerResponse,
   DbPool,
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::Follow,
@@ -40,6 +39,7 @@ use lemmy_utils::{
   get_apub_protocol_string,
   location_info,
   settings::Settings,
+  LemmyError,
   MentionData,
 };
 use log::debug;
diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs
index 606c4752..a54b8f6a 100644
--- a/server/src/apub/post.rs
+++ b/server/src/apub/post.rs
@@ -18,7 +18,6 @@ use crate::{
   blocking,
   DbPool,
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{
@@ -44,7 +43,7 @@ use lemmy_db::{
   user::User_,
   Crud,
 };
-use lemmy_utils::{convert_datetime, location_info, remove_slurs};
+use lemmy_utils::{convert_datetime, location_info, remove_slurs, LemmyError};
 use serde::Deserialize;
 use url::Url;
 
diff --git a/server/src/apub/private_message.rs b/server/src/apub/private_message.rs
index 1b3472e3..5563aef3 100644
--- a/server/src/apub/private_message.rs
+++ b/server/src/apub/private_message.rs
@@ -15,7 +15,6 @@ use crate::{
   blocking,
   DbPool,
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{
@@ -34,7 +33,7 @@ use lemmy_db::{
   user::User_,
   Crud,
 };
-use lemmy_utils::{convert_datetime, location_info};
+use lemmy_utils::{convert_datetime, location_info, LemmyError};
 use url::Url;
 
 #[async_trait::async_trait(?Send)]
diff --git a/server/src/apub/user.rs b/server/src/apub/user.rs
index a61813c1..5522a341 100644
--- a/server/src/apub/user.rs
+++ b/server/src/apub/user.rs
@@ -15,7 +15,6 @@ use crate::{
   blocking,
   DbPool,
   LemmyContext,
-  LemmyError,
 };
 use activitystreams::{
   activity::{
@@ -34,7 +33,7 @@ use lemmy_db::{
   naive_now,
   user::{UserForm, User_},
 };
-use lemmy_utils::{convert_datetime, location_info};
+use lemmy_utils::{convert_datetime, location_info, LemmyError};
 use serde::Deserialize;
 use url::Url;
 
diff --git a/server/src/code_migrations.rs b/server/src/code_migrations.rs
index d5139441..e59aa88c 100644
--- a/server/src/code_migrations.rs
+++ b/server/src/code_migrations.rs
@@ -1,5 +1,4 @@
 // This is for db migrations that require code
-use crate::LemmyError;
 use diesel::{
   sql_types::{Nullable, Text},
   *,
@@ -19,6 +18,7 @@ use lemmy_utils::{
   make_apub_endpoint,
   settings::Settings,
   EndpointType,
+  LemmyError,
 };
 use log::info;
 
diff --git a/server/src/lib.rs b/server/src/lib.rs
index 79182be9..380dc445 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -24,20 +24,19 @@ pub extern crate strum;
 pub mod api;
 pub mod apub;
 pub mod code_migrations;
-pub mod rate_limit;
 pub mod request;
 pub mod routes;
 pub mod version;
 pub mod websocket;
 
-use crate::request::{retry, RecvError};
-
-use crate::websocket::chat_server::ChatServer;
+use crate::{
+  request::{retry, RecvError},
+  websocket::chat_server::ChatServer,
+};
 use actix::Addr;
-use actix_web::dev::ConnectionInfo;
 use anyhow::anyhow;
 use background_jobs::QueueHandle;
-use lemmy_utils::{get_apub_protocol_string, settings::Settings};
+use lemmy_utils::{get_apub_protocol_string, settings::Settings, LemmyError};
 use log::error;
 use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
 use reqwest::Client;
@@ -45,33 +44,6 @@ use serde::Deserialize;
 use std::process::Command;
 
 pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
-pub type ConnectionId = usize;
-pub type PostId = i32;
-pub type CommunityId = i32;
-pub type UserId = i32;
-pub type IPAddr = String;
-
-#[derive(Debug)]
-pub struct LemmyError {
-  inner: anyhow::Error,
-}
-
-impl<T> From<T> for LemmyError
-where
-  T: Into<anyhow::Error>,
-{
-  fn from(t: T) -> Self {
-    LemmyError { inner: t.into() }
-  }
-}
-
-impl std::fmt::Display for LemmyError {
-  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-    self.inner.fmt(f)
-  }
-}
-
-impl actix_web::error::ResponseError for LemmyError {}
 
 pub struct LemmyContext {
   pub pool: DbPool,
@@ -252,16 +224,6 @@ pub async fn is_image_content_type(client: &Client, test: &str) -> Result<(), Le
   }
 }
 
-pub fn get_ip(conn_info: &ConnectionInfo) -> String {
-  conn_info
-    .realip_remote_addr()
-    .unwrap_or("127.0.0.1:12345")
-    .split(':')
-    .next()
-    .unwrap_or("127.0.0.1")
-    .to_string()
-}
-
 pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
 where
   F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
diff --git a/server/src/main.rs b/server/src/main.rs
index 30fcdaab..48549e46 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -18,17 +18,16 @@ use diesel::{
   PgConnection,
 };
 use lemmy_db::get_database_url_from_env;
+use lemmy_rate_limit::{rate_limiter::RateLimiter, RateLimit};
 use lemmy_server::{
   apub::activity_queue::create_activity_queue,
   blocking,
   code_migrations::run_advanced_migrations,
-  rate_limit::{rate_limiter::RateLimiter, RateLimit},
   routes::*,
   websocket::chat_server::ChatServer,
   LemmyContext,
-  LemmyError,
 };
-use lemmy_utils::{settings::Settings, CACHE_CONTROL_REGEX};
+use lemmy_utils::{settings::Settings, LemmyError, CACHE_CONTROL_REGEX};
 use reqwest::Client;
 use std::sync::Arc;
 use tokio::sync::Mutex;
diff --git a/server/src/request.rs b/server/src/request.rs
index 490609e7..137848f2 100644
--- a/server/src/request.rs
+++ b/server/src/request.rs
@@ -1,5 +1,5 @@
-use crate::LemmyError;
 use anyhow::anyhow;
+use lemmy_utils::LemmyError;
 use std::future::Future;
 use thiserror::Error;
 
diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs
index f2ee38d2..6a715f47 100644
--- a/server/src/routes/api.rs
+++ b/server/src/routes/api.rs
@@ -1,9 +1,7 @@
-use crate::{
-  api::{comment::*, community::*, post::*, site::*, user::*, Perform},
-  rate_limit::RateLimit,
-  LemmyContext,
-};
+use crate::{api::Perform, LemmyContext};
 use actix_web::{error::ErrorBadRequest, *};
+use lemmy_api_structs::{comment::*, community::*, post::*, site::*, user::*};
+use lemmy_rate_limit::RateLimit;
 use serde::Serialize;
 
 pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
diff --git a/server/src/routes/feeds.rs b/server/src/routes/feeds.rs
index 890a458f..9bfe4cb6 100644
--- a/server/src/routes/feeds.rs
+++ b/server/src/routes/feeds.rs
@@ -1,4 +1,4 @@
-use crate::{api::claims::Claims, blocking, LemmyContext, LemmyError};
+use crate::{api::claims::Claims, blocking, LemmyContext};
 use actix_web::{error::ErrorBadRequest, *};
 use anyhow::anyhow;
 use chrono::{DateTime, NaiveDateTime, Utc};
@@ -13,7 +13,7 @@ use lemmy_db::{
   ListingType,
   SortType,
 };
-use lemmy_utils::{markdown_to_html, settings::Settings};
+use lemmy_utils::{markdown_to_html, settings::Settings, LemmyError};
 use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder};
 use serde::Deserialize;
 use std::str::FromStr;
diff --git a/server/src/routes/images.rs b/server/src/routes/images.rs
index 766aff6e..2e5aa9b3 100644
--- a/server/src/routes/images.rs
+++ b/server/src/routes/images.rs
@@ -1,7 +1,7 @@
-use crate::rate_limit::RateLimit;
 use actix::clock::Duration;
 use actix_web::{body::BodyStream, http::StatusCode, *};
 use awc::Client;
+use lemmy_rate_limit::RateLimit;
 use lemmy_utils::settings::Settings;
 use serde::{Deserialize, Serialize};
 
diff --git a/server/src/routes/nodeinfo.rs b/server/src/routes/nodeinfo.rs
index 1c81bc54..1390b21e 100644
--- a/server/src/routes/nodeinfo.rs
+++ b/server/src/routes/nodeinfo.rs
@@ -1,8 +1,8 @@
-use crate::{blocking, version, LemmyContext, LemmyError};
+use crate::{blocking, version, LemmyContext};
 use actix_web::{body::Body, error::ErrorBadRequest, *};
 use anyhow::anyhow;
 use lemmy_db::site_view::SiteView;
-use lemmy_utils::{get_apub_protocol_string, settings::Settings};
+use lemmy_utils::{get_apub_protocol_string, settings::Settings, LemmyError};
 use serde::{Deserialize, Serialize};
 use url::Url;
 
diff --git a/server/src/routes/webfinger.rs b/server/src/routes/webfinger.rs
index 57bea713..22861434 100644
--- a/server/src/routes/webfinger.rs
+++ b/server/src/routes/webfinger.rs
@@ -1,8 +1,13 @@
-use crate::{blocking, LemmyContext, LemmyError};
+use crate::{blocking, LemmyContext};
 use actix_web::{error::ErrorBadRequest, web::Query, *};
 use anyhow::anyhow;
 use lemmy_db::{community::Community, user::User_};
-use lemmy_utils::{settings::Settings, WEBFINGER_COMMUNITY_REGEX, WEBFINGER_USER_REGEX};
+use lemmy_utils::{
+  settings::Settings,
+  LemmyError,
+  WEBFINGER_COMMUNITY_REGEX,
+  WEBFINGER_USER_REGEX,
+};
 use serde::{Deserialize, Serialize};
 
 #[derive(Deserialize)]
diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs
index 954b39b2..465974c0 100644
--- a/server/src/routes/websocket.rs
+++ b/server/src/routes/websocket.rs
@@ -1,5 +1,4 @@
 use crate::{
-  get_ip,
   websocket::{
     chat_server::ChatServer,
     messages::{Connect, Disconnect, StandardMessage, WSMessage},
@@ -9,6 +8,7 @@ use crate::{
 use actix::prelude::*;
 use actix_web::*;
 use actix_web_actors::ws;
+use lemmy_utils::get_ip;
 use log::{debug, error, info};
 use std::time::{Duration, Instant};
 
diff --git a/server/src/websocket/chat_server.rs b/server/src/websocket/chat_server.rs
index 92633c06..cb73b464 100644
--- a/server/src/websocket/chat_server.rs
+++ b/server/src/websocket/chat_server.rs
@@ -1,18 +1,10 @@
 use crate::{
-  api::{comment::*, community::*, post::*, site::*, user::*, APIError},
-  rate_limit::RateLimit,
   websocket::{
     handlers::{do_user_operation, to_json_string, Args},
     messages::*,
     UserOperation,
   },
-  CommunityId,
-  ConnectionId,
-  IPAddr,
   LemmyContext,
-  LemmyError,
-  PostId,
-  UserId,
 };
 use actix::prelude::*;
 use anyhow::Context as acontext;
@@ -21,7 +13,9 @@ use diesel::{
   r2d2::{ConnectionManager, Pool},
   PgConnection,
 };
-use lemmy_utils::location_info;
+use lemmy_api_structs::{comment::*, community::*, post::*, site::*, user::*, APIError};
+use lemmy_rate_limit::RateLimit;
+use lemmy_utils::{location_info, CommunityId, ConnectionId, IPAddr, LemmyError, PostId, UserId};
 use rand::rngs::ThreadRng;
 use reqwest::Client;
 use serde::Serialize;
diff --git a/server/src/websocket/handlers.rs b/server/src/websocket/handlers.rs
index 6d195ab0..a65e88bb 100644
--- a/server/src/websocket/handlers.rs
+++ b/server/src/websocket/handlers.rs
@@ -1,19 +1,17 @@
 use crate::{
   api::Perform,
-  rate_limit::RateLimit,
   websocket::{
     chat_server::{ChatServer, SessionInfo},
     messages::*,
     UserOperation,
   },
-  ConnectionId,
-  IPAddr,
   LemmyContext,
-  LemmyError,
 };
 use actix::{Actor, Context, Handler, ResponseFuture};
 use actix_web::web;
 use lemmy_db::naive_now;
+use lemmy_rate_limit::RateLimit;
+use lemmy_utils::{ConnectionId, IPAddr, LemmyError};
 use log::{error, info};
 use rand::Rng;
 use serde::{Deserialize, Serialize};
diff --git a/server/src/websocket/messages.rs b/server/src/websocket/messages.rs
index 1289017c..2a0e5fde 100644
--- a/server/src/websocket/messages.rs
+++ b/server/src/websocket/messages.rs
@@ -1,13 +1,7 @@
-use crate::{
-  api::{comment::CommentResponse, post::PostResponse},
-  websocket::UserOperation,
-  CommunityId,
-  ConnectionId,
-  IPAddr,
-  PostId,
-  UserId,
-};
+use crate::websocket::UserOperation;
 use actix::{prelude::*, Recipient};
+use lemmy_api_structs::{comment::CommentResponse, post::PostResponse};
+use lemmy_utils::{CommunityId, ConnectionId, IPAddr, PostId, UserId};
 use serde::{Deserialize, Serialize};
 
 /// Chat server sends this messages to session
-- 
2.44.1