]> Untitled Git - lemmy.git/commitdiff
Merge branch 'main' into move_views_to_diesel
authorDessalines <tyhou13@gmx.com>
Wed, 16 Dec 2020 19:01:16 +0000 (14:01 -0500)
committerDessalines <tyhou13@gmx.com>
Wed, 16 Dec 2020 19:01:16 +0000 (14:01 -0500)
ansible/templates/nginx.conf
docker/dev/docker-compose.yml
docker/federation/nginx.conf
docs/src/SUMMARY.md
docs/src/contributing/custom_frontend.md [new file with mode: 0644]
lemmy_db/src/source/comment.rs
lemmy_db/src/source/community.rs
lemmy_db/src/source/post.rs
lemmy_db/src/source/user_mention.rs
lemmy_structs/src/lib.rs

index 0874be8f9c853fecd5b9223d6a4c11439a0f0c50..84b9c665648830977465113cfb4e8c2e7c30ceea 100644 (file)
@@ -61,12 +61,20 @@ server {
       if ($http_accept = "application/activity+json") {
         set $proxpass "http://0.0.0.0:{{ lemmy_port }}";
       }
+      if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+        set $proxpass "http://0.0.0.0:{{ lemmy_port }}";
+      }
       if ($request_method = POST) {
         set $proxpass "http://0.0.0.0:{{ lemmy_port }}";
       }
       proxy_pass $proxpass;
 
       rewrite ^(.+)/+$ $1 permanent;
+
+      # Send actual client IP upstream
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header Host $host;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     }
 
     # backend
index a09fb8eb1bb51e0d71a4686d17473be91299edbc..5342f8b10009a948e166d8a22f30e03135d42588 100644 (file)
@@ -15,6 +15,7 @@ services:
       - pictrs
       - postgres
       - iframely
+
   lemmy-ui:
     image: dessalines/lemmy-ui:v0.8.10
     ports:
index 003f88dc027b1712e28d027c461a838e04cb85a9..357b87c90ec952ef4b9e8a08b851732052486818 100644 (file)
@@ -28,6 +28,9 @@ http {
             if ($http_accept = "application/activity+json") {
               set $proxpass http://lemmy-alpha;
             }
+            if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+              set $proxpass http://lemmy-alpha;
+            }
             proxy_pass $proxpass;
 
             proxy_set_header X-Real-IP $remote_addr;
@@ -70,6 +73,9 @@ http {
             if ($http_accept = "application/activity+json") {
               set $proxpass http://lemmy-beta;
             }
+            if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+              set $proxpass http://lemmy-beta;
+            }
             proxy_pass $proxpass;
 
             proxy_set_header X-Real-IP $remote_addr;
@@ -112,6 +118,9 @@ http {
             if ($http_accept = "application/activity+json") {
               set $proxpass http://lemmy-gamma;
             }
+            if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+              set $proxpass http://lemmy-gamma;
+            }
             proxy_pass $proxpass;
 
             proxy_set_header X-Real-IP $remote_addr;
@@ -154,6 +163,9 @@ http {
             if ($http_accept = "application/activity+json") {
               set $proxpass http://lemmy-delta;
             }
+            if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+              set $proxpass http://lemmy-delta;
+            }
             proxy_pass $proxpass;
 
             proxy_set_header X-Real-IP $remote_addr;
@@ -196,6 +208,9 @@ http {
             if ($http_accept = "application/activity+json") {
               set $proxpass http://lemmy-epsilon;
             }
+            if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
+              set $proxpass http://lemmy-epsilon;
+            }
             proxy_pass $proxpass;
 
             proxy_set_header X-Real-IP $remote_addr;
index 93f6c42d0692f9cd8a5abd6123983989b8ba78e8..c8b76f7c3c1a9b07da782491d3046ed9160beb52 100644 (file)
@@ -19,7 +19,8 @@
   - [Docker Development](contributing/docker_development.md)
   - [Local Development](contributing/local_development.md)
   - [Theming Guide](contributing/theming.md)
-  - [Tests](contributing/tests.md)
   - [Websocket/HTTP API](contributing/websocket_http_api.md)
+  - [Creating a Custom Frontend](contributing/custom_frontend.md)
+  - [Tests](contributing/tests.md)
   - [Federation Development](contributing/federation_development.md)
 - [Lemmy Council](lemmy_council.md)
diff --git a/docs/src/contributing/custom_frontend.md b/docs/src/contributing/custom_frontend.md
new file mode 100644 (file)
index 0000000..0856857
--- /dev/null
@@ -0,0 +1,66 @@
+# Creating a Custom Frontend
+
+The backend and frontend are completely decoupled, and run in independent Docker containers. They only communicate over the [Lemmy API](websocket_http_api.md), which makes it quite easy to write alternative frontends.
+
+This creates a lot of potential for custom frontends, which could change much of the design and user experience of Lemmy. For example, it would be possible to create a frontend in the style of a traditional forum like [phpBB](https://www.phpbb.com/), or a question-and-answer site like [stackoverflow](https://stackoverflow.com/). All without having to think about database queries, authentification or ActivityPub, which you essentially get for free.
+
+## Development
+
+You can use any language to create a custom frontend. The easiest option would be forking our [official frontend](https://github.com/LemmyNet/lemmy-ui), [lemmy-lite](https://github.com/IronOxidizer/lemmy-lite), or the [lemmy-frontend-example](https://github.com/LemmyNet/lemmy-front-end-example). In any case, the principle is the same: bind to `LEMMY_EXTERNAL_HOST` (default: `localhost:8536`) and handle requests using the Lemmy API at `LEMMY_INTERNAL_HOST` (default: `lemmy:8536`). Also use `LEMMY_HTTPS` to generate links with the correct protocol.
+
+The next step is building a Docker image from your frontend. If you forked an existing project, it should already include a `Dockerfile` and instructions to build it. Otherwise, try searching for your language on [dockerhub](https://hub.docker.com/), official images usually have build instructions in their readme. Build a Docker image with a tag, then look for the following section in `docker/dev/docker-compose.yml`:
+
+```
+  lemmy-ui:
+    image: dessalines/lemmy-ui:v0.8.10
+    ports:
+      - "1235:1234"
+    restart: always
+    environment:
+      - LEMMY_INTERNAL_HOST=lemmy:8536
+      - LEMMY_EXTERNAL_HOST=localhost:8536
+      - LEMMY_HTTPS=false
+    depends_on: 
+      - lemmy
+```
+
+All you need to do is replace the value for `image` with the tag of your own Docker image (and possibly the environment variables if you need different ones). Then run `./docker_update.sh`, and after compilation, your frontend will be available on `http://localhost:1235`. You can also make the same change to `docker/federation/docker-compose.yml` and run `./start-local-instances.bash` to test federation with your frontend.
+
+## Deploy with Docker
+
+After building the Docker image, you need to push it to a Docker registry (such as [dockerhub](https://hub.docker.com/)). Then update the `docker-compose.yml` on your server, replacing the `image` for `lemmy-ui`, just as described above. Run `docker-compose.yml`, and after a short wait, your instance will use the new frontend.
+
+Note, if your instance is deployed with Ansible, it will override `docker-compose.yml` with every run, reverting back to the default frontend. In that case you should copy the `ansible/` folder from this project to your own repository, and adjust `docker-compose.yml` directly in the repo.
+
+It is also possible to use multiple frontends for the same Lemmy instance, either using subdomains or subfolders. To do that, don't edit the `lemmy-ui` section in `docker-compose.yml`, but duplicate it, adjusting the name, image and port so they are distinct for each. Then edit your nginx config to pass requests to the appropriate frontend, depending on the subdomain or path.
+
+## Translations
+
+You can add the [lemmy-translations](https://github.com/LemmyNet/lemmy-translations) repository to your project as a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules). That way you can take advantage of same translations used in the official frontend, and you will also receive new translations contributed via weblate.
+
+## Rate limiting
+
+Lemmy does rate limiting for many actions based on the client IP. But if you make any API calls on the server side (eg in the case of server-side rendering, or javascript pre-rendering), Lemmy will take the IP of the Docker container. Meaning that all requests come from the same IP, and get rate limited much earlier. To avoid this problem, you need to pass the headers `X-REAL-IP` and `X-FORWARDED-FOR` on to Lemmy (the headers are set by our nginx config).
+
+Here is an example snipped for NodeJS:
+
+```javascript
+function setForwardedHeaders(
+  headers: IncomingHttpHeaders
+): { [key: string]: string } {
+  let out = {
+    host: headers.host,
+  };
+  if (headers['x-real-ip']) {
+    out['x-real-ip'] = headers['x-real-ip'];
+  }
+  if (headers['x-forwarded-for']) {
+    out['x-forwarded-for'] = headers['x-forwarded-for'];
+  }
+
+  return out;
+}
+
+let headers = setForwardedHeaders(req.headers);
+let client = new LemmyHttp(httpUri, headers);
+```
\ No newline at end of file
index 239762599a010e4156c773660aab68ffb4dc860e..380bdb3d157db6b1a259cc81dcf8416549c3cfa3 100644 (file)
@@ -228,6 +228,9 @@ impl Likeable<CommentLikeForm> for CommentLike {
     use crate::schema::comment_like::dsl::*;
     insert_into(comment_like)
       .values(comment_like_form)
+      .on_conflict((comment_id, user_id))
+      .do_update()
+      .set(comment_like_form)
       .get_result::<Self>(conn)
   }
   fn remove(conn: &PgConnection, user_id: i32, comment_id: i32) -> Result<usize, Error> {
@@ -263,6 +266,9 @@ impl Saveable<CommentSavedForm> for CommentSaved {
     use crate::schema::comment_saved::dsl::*;
     insert_into(comment_saved)
       .values(comment_saved_form)
+      .on_conflict((comment_id, user_id))
+      .do_update()
+      .set(comment_saved_form)
       .get_result::<Self>(conn)
   }
   fn unsave(conn: &PgConnection, comment_saved_form: &CommentSavedForm) -> Result<usize, Error> {
index ea7a028e1f863e2831edea344b1cdb0d11eee31b..84db0c7c418c70f5056a69a867b0ae8a23123d99 100644 (file)
@@ -375,6 +375,9 @@ impl Followable<CommunityFollowerForm> for CommunityFollower {
     use crate::schema::community_follower::dsl::*;
     insert_into(community_follower)
       .values(community_follower_form)
+      .on_conflict((community_id, user_id))
+      .do_update()
+      .set(community_follower_form)
       .get_result::<Self>(conn)
   }
   fn follow_accepted(conn: &PgConnection, community_id_: i32, user_id_: i32) -> Result<Self, Error>
index b584798e87a98cc884abdb0cbe15bca2b166e44a..098ce8835316db5a72a6c005773e3ce4a3581634 100644 (file)
@@ -241,6 +241,9 @@ impl Likeable<PostLikeForm> for PostLike {
     use crate::schema::post_like::dsl::*;
     insert_into(post_like)
       .values(post_like_form)
+      .on_conflict((post_id, user_id))
+      .do_update()
+      .set(post_like_form)
       .get_result::<Self>(conn)
   }
   fn remove(conn: &PgConnection, user_id: i32, post_id: i32) -> Result<usize, Error> {
@@ -276,6 +279,9 @@ impl Saveable<PostSavedForm> for PostSaved {
     use crate::schema::post_saved::dsl::*;
     insert_into(post_saved)
       .values(post_saved_form)
+      .on_conflict((post_id, user_id))
+      .do_update()
+      .set(post_saved_form)
       .get_result::<Self>(conn)
   }
   fn unsave(conn: &PgConnection, post_saved_form: &PostSavedForm) -> Result<usize, Error> {
index ed5c2c797bc8c0ac5a2ccdfb0d3f608b7fb0fda6..bf53cb4200504854fbdaecce9515158fe1eec54a 100644 (file)
@@ -30,8 +30,13 @@ impl Crud<UserMentionForm> for UserMention {
 
   fn create(conn: &PgConnection, user_mention_form: &UserMentionForm) -> Result<Self, Error> {
     use crate::schema::user_mention::dsl::*;
+    // since the return here isnt utilized, we dont need to do an update
+    // but get_result doesnt return the existing row here
     insert_into(user_mention)
       .values(user_mention_form)
+      .on_conflict((recipient_id, comment_id))
+      .do_update()
+      .set(user_mention_form)
       .get_result::<Self>(conn)
   }
 
index 3a2e28d94ce0b175a08f5f60741e5e94b5796eed..dc06a40cd1c05415353bf5b08b203998df6c8829 100644 (file)
@@ -100,10 +100,7 @@ fn do_send_local_notifs(
 
       // Allow this to fail softly, since comment edits might re-update or replace it
       // Let the uniqueness handle this fail
-      match UserMention::create(&conn, &user_mention_form) {
-        Ok(_mention) => (),
-        Err(_e) => error!("{}", &_e),
-      };
+      let _ = UserMention::create(&conn, &user_mention_form);
 
       // Send an email to those users that have notifications on
       if do_send_email && mention_user.send_notifications_to_email {