Browse Source

Isomorphic docker (#1124)

* Adding a way to GetComments for a community given its name only.

* Adding getcomments to api docs.

* A first pass at locally working isomorphic integration.

* Testing out cargo-husky.

* Testing a fail hook.

* Revert "Testing a fail hook."

This reverts commit 0941cf1736.

* Moving server to top level, now that UI is gone.

* Running cargo fmt using old way.

* Adding nginx, fixing up docker-compose files, fixing docs.

* Trying to re-add API tests.

* Fixing prod dockerfile.

* Redoing nightly fmt

* Trying to fix private message api test.

* Adding CommunityJoin, PostJoin instead of joins from GetComments, etc.

- Fixes #1122

* Fixing fmt.

* Fixing up docs.

* Removing translations.

* Adding apps / clients to readme.

* Fixing main image.

* Using new lemmy-isomorphic-ui with better javascript disabled.

* Try to fix image uploads in federation test

* Revert "Try to fix image uploads in federation test"

This reverts commit a2ddf2a90b.

* Fix post url federation

* Adding some more tests, some still broken.

* Don't need gitattributes anymore.

* Update local federation test setup

* Fixing tests.

* Fixing travis build.

* Fixing travis build, again.

* Changing lemmy-isomorphic-ui to lemmy-ui

* Error in travis build again.

Co-authored-by: Felix Ableitner <me@nutomic.com>
tags/v0.7.59
Dessalines 1 month ago
committed by GitHub
parent
commit
5c6258390c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 4268 additions and 343 deletions
  1. +0
    -2
      .gitattributes
  2. +10
    -9
      .gitignore
  3. +0
    -0
      .rustfmt.toml
  4. +7
    -0
      Cargo.lock
  5. +5
    -0
      Cargo.toml
  6. +4
    -1
      README.md
  7. +0
    -2
      ansible/templates/config.hjson
  8. +34
    -16
      ansible/templates/nginx.conf
  9. +4
    -0
      api_tests/jest.config.js
  10. +20
    -0
      api_tests/package.json
  11. +2
    -2
      api_tests/src/comment.spec.ts
  12. +13
    -0
      api_tests/src/community.spec.ts
  13. +1
    -0
      api_tests/src/follow.spec.ts
  14. +4
    -0
      api_tests/src/post.spec.ts
  15. +1
    -0
      api_tests/src/private_message.spec.ts
  16. +29
    -6
      api_tests/src/shared.ts
  17. +2
    -1
      api_tests/src/user.spec.ts
  18. +3659
    -0
      api_tests/yarn.lock
  19. +0
    -0
      clean.sh
  20. +0
    -0
      config/config.hjson
  21. +0
    -2
      config/defaults.hjson
  22. +0
    -0
      db-init.sh
  23. +0
    -0
      diesel.toml
  24. +13
    -26
      docker/dev/Dockerfile
  25. +10
    -0
      docker/dev/docker-compose.yml
  26. +60
    -15
      docker/federation/docker-compose.yml
  27. +35
    -35
      docker/federation/nginx.conf
  28. +7
    -6
      docker/federation/run-tests.sh
  29. +0
    -2
      docker/lemmy.hjson
  30. +9
    -22
      docker/prod/Dockerfile
  31. +2
    -2
      docker/prod/deploy.sh
  32. +11
    -0
      docker/prod/docker-compose.yml
  33. +20
    -49
      docker/travis/docker-compose.yml
  34. +6
    -6
      docker/travis/run-tests.sh
  35. +1
    -0
      docs/src/about_features.md
  36. +1
    -1
      docs/src/administration_configuration.md
  37. +4
    -3
      docs/src/contributing.md
  38. +3
    -1
      docs/src/contributing_docker_development.md
  39. +15
    -20
      docs/src/contributing_local_development.md
  40. +1
    -1
      docs/src/contributing_tests.md
  41. +3
    -2
      docs/src/contributing_theming.md
  42. +187
    -57
      docs/src/contributing_websocket_http_api.md
  43. +1
    -1
      install.sh
  44. +0
    -0
      lemmy_api_structs/Cargo.toml
  45. +1
    -0
      lemmy_api_structs/src/comment.rs
  46. +10
    -0
      lemmy_api_structs/src/community.rs
  47. +18
    -18
      lemmy_api_structs/src/lib.rs
  48. +10
    -0
      lemmy_api_structs/src/post.rs
  49. +0
    -0
      lemmy_api_structs/src/site.rs
  50. +1
    -1
      lemmy_api_structs/src/user.rs
  51. +0
    -0
      lemmy_db/Cargo.toml
  52. +0
    -0
      lemmy_db/src/activity.rs
  53. +0
    -0
      lemmy_db/src/category.rs
  54. +0
    -0
      lemmy_db/src/comment.rs
  55. +11
    -0
      lemmy_db/src/comment_view.rs
  56. +0
    -0
      lemmy_db/src/community.rs
  57. +0
    -0
      lemmy_db/src/community_view.rs
  58. +0
    -0
      lemmy_db/src/lib.rs
  59. +0
    -0
      lemmy_db/src/moderator.rs
  60. +0
    -0
      lemmy_db/src/moderator_views.rs
  61. +0
    -0
      lemmy_db/src/password_reset_request.rs
  62. +0
    -0
      lemmy_db/src/post.rs
  63. +0
    -0
      lemmy_db/src/post_view.rs
  64. +0
    -0
      lemmy_db/src/private_message.rs
  65. +0
    -0
      lemmy_db/src/private_message_view.rs
  66. +32
    -32
      lemmy_db/src/schema.rs
  67. +0
    -0
      lemmy_db/src/site.rs
  68. +0
    -0
      lemmy_db/src/site_view.rs
  69. +0
    -0
      lemmy_db/src/user.rs
  70. +0
    -0
      lemmy_db/src/user_mention.rs
  71. +0
    -0
      lemmy_db/src/user_mention_view.rs
  72. +0
    -0
      lemmy_db/src/user_view.rs
  73. +0
    -0
      lemmy_rate_limit/Cargo.toml
  74. +0
    -0
      lemmy_rate_limit/src/lib.rs
  75. +0
    -0
      lemmy_rate_limit/src/rate_limiter.rs
  76. +0
    -0
      lemmy_utils/Cargo.toml
  77. +0
    -0
      lemmy_utils/src/apub.rs
  78. +0
    -0
      lemmy_utils/src/email.rs
  79. +0
    -0
      lemmy_utils/src/lib.rs
  80. +1
    -2
      lemmy_utils/src/settings.rs
  81. +0
    -0
      lemmy_utils/src/test.rs
  82. +0
    -0
      lemmy_utils/src/utils.rs
  83. +0
    -0
      migrations/.gitkeep
  84. +0
    -0
      migrations/00000000000000_diesel_initial_setup/down.sql
  85. +0
    -0
      migrations/00000000000000_diesel_initial_setup/up.sql
  86. +0
    -0
      migrations/2019-02-26-002946_create_user/down.sql
  87. +0
    -0
      migrations/2019-02-26-002946_create_user/up.sql
  88. +0
    -0
      migrations/2019-02-27-170003_create_community/down.sql
  89. +0
    -0
      migrations/2019-02-27-170003_create_community/up.sql
  90. +0
    -0
      migrations/2019-03-03-163336_create_post/down.sql
  91. +0
    -0
      migrations/2019-03-03-163336_create_post/up.sql
  92. +0
    -0
      migrations/2019-03-05-233828_create_comment/down.sql
  93. +0
    -0
      migrations/2019-03-05-233828_create_comment/up.sql
  94. +0
    -0
      migrations/2019-03-30-212058_create_post_view/down.sql
  95. +0
    -0
      migrations/2019-03-30-212058_create_post_view/up.sql
  96. +0
    -0
      migrations/2019-04-03-155205_create_community_view/down.sql
  97. +0
    -0
      migrations/2019-04-03-155205_create_community_view/up.sql
  98. +0
    -0
      migrations/2019-04-03-155309_create_comment_view/down.sql
  99. +0
    -0
      migrations/2019-04-03-155309_create_comment_view/up.sql
  100. +0
    -0
      migrations/2019-04-07-003142_create_moderation_logs/down.sql

+ 0
- 2
.gitattributes View File

@@ -1,2 +0,0 @@
* linguist-vendored
*.rs linguist-vendored=false

+ 10
- 9
.gitignore View File

@@ -6,16 +6,17 @@ ansible/passwords/
# docker build files
docker/lemmy_mine.hjson
docker/dev/env_deploy.sh
docker/federation/volumes
docker/federation-test/volumes
docker/dev/volumes

# local build files
build/
ui/src/translations
volumes

# ide config
.idea/
.vscode/
.idea
.vscode

# local build files
target
env_setup.sh
query_testing/*.json
query_testing/*.json.old

# API tests
api_tests/node_modules

server/.rustfmt.toml → .rustfmt.toml View File


server/Cargo.lock → Cargo.lock View File

@@ -794,6 +794,12 @@ dependencies = [
"time 0.1.44",
]

[[package]]
name = "cargo-husky"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b02b629252fe8ef6460461409564e2c21d0c8e77e0944f3d189ff06c4e932ad"

[[package]]
name = "cc"
version = "1.0.59"
@@ -1875,6 +1881,7 @@ dependencies = [
"base64 0.12.3",
"bcrypt",
"captcha",
"cargo-husky",
"chrono",
"diesel",
"diesel_migrations",

server/Cargo.toml → Cargo.toml View File

@@ -59,3 +59,8 @@ anyhow = "1.0.32"
thiserror = "1.0.20"
background-jobs = " 0.8.0-alpha.2"
reqwest = { version = "0.10", features = ["json"] }

[dev-dependencies.cargo-husky]
version = "1"
default-features = false # Disable features which are enabled by default
features = ["precommit-hook", "run-cargo-fmt", "run-cargo-clippy"]

+ 4
- 1
README.md View File

@@ -11,7 +11,7 @@

<p align="center">
<a href="https://dev.lemmy.ml/" rel="noopener">
<img width=200px height=200px src="ui/assets/favicon.svg"></a>
<img width=200px height=200px src="https://raw.githubusercontent.com/LemmyNet/lemmy-ui/main/src/assets/icons/favicon.svg"></a>

<h3 align="center"><a href="https://dev.lemmy.ml">Lemmy</a></h3>
<p align="center">
@@ -108,12 +108,15 @@ Each Lemmy server can set its own moderation policy; appointing site-wide admins

### Apps

- [lemmy-ui - The official web app for lemmy](https://github.com/LemmyNet/lemmy-ui)
- [Lemmur - A flutter lemmy app ( under development )](https://github.com/krawieck/lemmur)
- [Lemmy-mobile (Android / IOS) - React native ( under development )](https://github.com/koredefashokun/lemmy-mobile)

### Libraries

- [lemmy-js-client](https://github.com/LemmyNet/lemmy-js-client)
- [Kotlin API ( under development )](https://github.com/eiknat/lemmy-client)
- [Dart API client ( under development )](https://github.com/krawieck/lemmy_api_client)

## Support / Donate



+ 0
- 2
ansible/templates/config.hjson View File

@@ -13,8 +13,6 @@
hostname: "{{ domain }}"
# json web token for authorization between server and client
jwt_secret: "{{ jwt_password }}"
# The location of the frontend
front_end_dir: "/app/dist"
# email sending configuration
email: {
# hostname of the smtp server


+ 34
- 16
ansible/templates/nginx.conf View File

@@ -51,23 +51,41 @@ server {
# Upload limit for pictrs
client_max_body_size 20M;

# lemmy api
location /api/v1 {
proxy_pass http://0.0.0.0:8536/api/v1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Cuts off the trailing slash on URLs to make them valid
rewrite ^(.+)/+$ $1 permanent;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# Rate limit
limit_req zone=lemmy_ratelimit burst=30 nodelay;
}

# Docs
location /docs {
proxy_pass http://0.0.0.0:8536/docs;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# lemmy-ui
location / {
proxy_pass http://0.0.0.0:8536;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Cuts off the trailing slash on URLs to make them valid
rewrite ^(.+)/+$ $1 permanent;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# Rate limit
limit_req zone=lemmy_ratelimit burst=30 nodelay;
}
proxy_pass http://0.0.0.0:1235;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}


# Redirect pictshare images to pictrs
location ~ /pictshare/(.*)$ {


+ 4
- 0
api_tests/jest.config.js View File

@@ -0,0 +1,4 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

+ 20
- 0
api_tests/package.json View File

@@ -0,0 +1,20 @@
{
"name": "api_tests",
"version": "0.0.1",
"description": "API tests for lemmy backend",
"main": "index.js",
"repository": "https://github.com/LemmyNet/lemmy",
"author": "Dessalines",
"license": "AGPL-3.0",
"scripts": {
"api-test": "jest src/ -i --verbose"
},
"devDependencies": {
"@types/jest": "^26.0.13",
"jest": "^26.4.2",
"lemmy-js-client": "^1.0.11",
"node-fetch": "^2.6.1",
"ts-jest": "^26.3.0",
"typescript": "^4.0.2"
}
}

ui/src/api_tests/comment.spec.ts → api_tests/src/comment.spec.ts View File

@@ -235,7 +235,7 @@ test('Reply to a comment', async () => {

test('Mention beta', async () => {
// Create a mention on alpha
let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8550';
let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8551';
let commentRes = await createComment(alpha, postRes.post.id);
await delay();
let mentionRes = await createComment(
@@ -275,7 +275,7 @@ test('A and G subscribe to B (center) A posts, G mentions B, it gets announced t
let gammaPost = search.posts[0];

let commentContent =
'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8550';
'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8551';
let commentRes = await createComment(
gamma,
gammaPost.id,

ui/src/api_tests/community.spec.ts → api_tests/src/community.spec.ts View File

@@ -1,8 +1,10 @@
jest.setTimeout(120000);
import {
alpha,
beta,
setupLogins,
searchForBetaCommunity,
searchForCommunity,
createCommunity,
deleteCommunity,
removeCommunity,
@@ -21,6 +23,17 @@ test('Create community', async () => {
let prevName = communityRes.community.name;
let communityRes2 = await createCommunity(alpha, prevName);
expect(communityRes2['error']).toBe('community_already_exists');
await delay();

// Cache the community on beta, make sure it has the other fields
let searchShort = `!${prevName}@lemmy-alpha:8541`;
let search = await searchForCommunity(beta, searchShort);
let communityOnBeta = search.communities[0];
expect(communityOnBeta.name).toBe(communityRes.community.name);
expect(communityOnBeta.title).toBe(communityRes.community.title);
expect(communityOnBeta.description).toBe(communityRes.community.description);
expect(communityOnBeta.icon).toBe(communityRes.community.icon);
expect(communityOnBeta.banner).toBe(communityRes.community.banner);
});

test('Delete community', async () => {

ui/src/api_tests/follow.spec.ts → api_tests/src/follow.spec.ts View File

@@ -1,3 +1,4 @@
jest.setTimeout(120000);
import {
alpha,
setupLogins,

ui/src/api_tests/post.spec.ts → api_tests/src/post.spec.ts View File

@@ -56,6 +56,10 @@ test('Create a post', async () => {
expect(betaPost.community_local).toBe(true);
expect(betaPost.creator_local).toBe(false);
expect(betaPost.score).toBe(1);
expect(betaPost.name).toBe(postRes.post.name);
expect(betaPost.body).toBe(postRes.post.body);
expect(betaPost.url).toBe(postRes.post.url);
expect(betaPost.nsfw).toBe(postRes.post.nsfw);

// Delta only follows beta, so it should not see an alpha ap_id
let searchDelta = await searchPost(delta, postRes.post);

ui/src/api_tests/private_message.spec.ts → api_tests/src/private_message.spec.ts View File

@@ -1,3 +1,4 @@
jest.setTimeout(120000);
import {
alpha,
beta,

ui/src/api_tests/shared.ts → api_tests/src/shared.ts View File

@@ -50,23 +50,23 @@ export interface API {
}

export let alpha: API = {
client: new LemmyHttp('http://localhost:8540/api/v1'),
client: new LemmyHttp('http://localhost:8541/api/v1'),
};

export let beta: API = {
client: new LemmyHttp('http://localhost:8550/api/v1'),
client: new LemmyHttp('http://localhost:8551/api/v1'),
};

export let gamma: API = {
client: new LemmyHttp('http://localhost:8560/api/v1'),
client: new LemmyHttp('http://localhost:8561/api/v1'),
};

export let delta: API = {
client: new LemmyHttp('http://localhost:8570/api/v1'),
client: new LemmyHttp('http://localhost:8571/api/v1'),
};

export let epsilon: API = {
client: new LemmyHttp('http://localhost:8580/api/v1'),
client: new LemmyHttp('http://localhost:8581/api/v1'),
};

export async function setupLogins() {
@@ -120,8 +120,12 @@ export async function createPost(
community_id: number
): Promise<PostResponse> {
let name = 'A jest test post';
let body = 'Some body';
let url = 'https://google.com/';
let form: PostForm = {
name,
url,
body,
auth: api.auth,
community_id,
nsfw: false,
@@ -232,7 +236,20 @@ export async function searchForBetaCommunity(
// Make sure lemmy-beta/c/main is cached on lemmy_alpha
// Use short-hand search url
let form: SearchForm = {
q: '!main@lemmy-beta:8550',
q: '!main@lemmy-beta:8551',
type_: SearchType.Communities,
sort: SortType.TopAll,
};
return api.client.search(form);
}

export async function searchForCommunity(
api: API,
q: string,
): Promise<SearchResponse> {
// Use short-hand search url
let form: SearchForm = {
q,
type_: SearchType.Communities,
sort: SortType.TopAll,
};
@@ -369,9 +386,15 @@ export async function createCommunity(
api: API,
name_: string = randomString(5)
): Promise<CommunityResponse> {
let description = 'a sample description';
let icon = 'https://image.flaticon.com/icons/png/512/35/35896.png';
let banner = 'https://image.flaticon.com/icons/png/512/35/35896.png';
let form: CommunityForm = {
name: name_,
title: name_,
description,
icon,
banner,
category_id: 1,
nsfw: false,
auth: api.auth,

ui/src/api_tests/user.spec.ts → api_tests/src/user.spec.ts View File

@@ -1,3 +1,4 @@
jest.setTimeout(120000);
import {
alpha,
beta,
@@ -17,7 +18,7 @@ test('Create user', async () => {

let site = await getSite(alpha, auth);
expect(site.my_user).toBeDefined();
apShortname = `@${site.my_user.name}@lemmy-alpha:8540`;
apShortname = `@${site.my_user.name}@lemmy-alpha:8541`;
});

test('Save user settings, check changed bio from beta', async () => {

+ 3659
- 0
api_tests/yarn.lock
File diff suppressed because it is too large
View File


server/clean.sh → clean.sh View File


server/config/config.hjson → config/config.hjson View File


server/config/defaults.hjson → config/defaults.hjson View File

@@ -33,8 +33,6 @@
port: 8536
# json web token for authorization between server and client
jwt_secret: "changeme"
# The location of the frontend
front_end_dir: "../ui/dist"
# address where pictrs is available
pictrs_url: "http://pictrs:8080"
# rate limits for various user actions, by user ip

server/db-init.sh → db-init.sh View File


server/diesel.toml → diesel.toml View File


+ 13
- 26
docker/dev/Dockerfile View File

@@ -1,15 +1,3 @@
FROM node:10-jessie as node

WORKDIR /app/ui

# Cache deps
COPY ui/package.json ui/yarn.lock ./
RUN yarn install --pure-lockfile

# Build
COPY ui /app/ui
RUN yarn build

FROM ekidd/rust-musl-builder:nightly-2020-05-07 as rust

# Cache deps
@@ -27,22 +15,22 @@ RUN mkdir -p lemmy_db/src/ \
lemmy

# Copy the cargo tomls
COPY server/Cargo.toml server/Cargo.lock ./
COPY server/lemmy_db/Cargo.toml ./lemmy_db/
COPY server/lemmy_utils/Cargo.toml ./lemmy_utils/
COPY server/lemmy_api_structs/Cargo.toml ./lemmy_api_structs/
COPY server/lemmy_rate_limit/Cargo.toml ./lemmy_rate_limit/
COPY Cargo.toml Cargo.lock ./
COPY lemmy_db/Cargo.toml ./lemmy_db/
COPY lemmy_utils/Cargo.toml ./lemmy_utils/
COPY lemmy_api_structs/Cargo.toml ./lemmy_api_structs/
COPY lemmy_rate_limit/Cargo.toml ./lemmy_rate_limit/

# Cache the deps
RUN cargo build-deps

# Copy the src folders
COPY server/src ./src/
COPY server/lemmy_db/src ./lemmy_db/src/
COPY server/lemmy_utils/src/ ./lemmy_utils/src/
COPY server/lemmy_api_structs/src/ ./lemmy_api_structs/src/
COPY server/lemmy_rate_limit/src/ ./lemmy_rate_limit/src/
COPY server/migrations ./migrations/
COPY src ./src/
COPY lemmy_db/src ./lemmy_db/src/
COPY lemmy_utils/src/ ./lemmy_utils/src/
COPY lemmy_api_structs/src/ ./lemmy_api_structs/src/
COPY lemmy_rate_limit/src/ ./lemmy_rate_limit/src/
COPY migrations ./migrations/

# Build for debug
RUN cargo build
@@ -62,10 +50,9 @@ RUN apk add libpq
RUN apk add espeak

# Copy resources
COPY server/config/defaults.hjson /config/defaults.hjson
COPY config/defaults.hjson /config/defaults.hjson
COPY --from=rust /app/server/target/x86_64-unknown-linux-musl/debug/lemmy_server /app/lemmy
COPY --from=docs /app/docs/book/ /app/dist/documentation/
COPY --from=node /app/ui/dist /app/dist
COPY --from=docs /app/docs/book/ /app/documentation/

RUN addgroup -g 1000 lemmy
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy


+ 10
- 0
docker/dev/docker-compose.yml View File

@@ -15,6 +15,16 @@ services:
- pictrs
- postgres
- iframely
lemmy-ui:
image: dessalines/lemmy-ui:v0.0.14
ports:
- "1235:1234"
environment:
- LEMMY_INTERNAL_HOST=lemmy:8536
- LEMMY_EXTERNAL_HOST=localhost:8536
- LEMMY_HTTPS=false
depends_on:
- lemmy

postgres:
image: postgres:12-alpine


+ 60
- 15
docker/federation/docker-compose.yml View File

@@ -28,17 +28,24 @@ services:
volumes:
- ./volumes/pictrs_alpha:/mnt

lemmy-alpha-ui:
image: dessalines/lemmy-ui:v0.0.14
environment:
- LEMMY_INTERNAL_HOST=lemmy-alpha:8541
- LEMMY_EXTERNAL_HOST=localhost:8541
- LEMMY_HTTPS=false
depends_on:
- lemmy-alpha
lemmy-alpha:
image: lemmy-federation:latest
environment:
- LEMMY_HOSTNAME=lemmy-alpha:8540
- LEMMY_HOSTNAME=lemmy-alpha:8541
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8540
- LEMMY_PORT=8541
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
@@ -49,6 +56,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_alpha
ports:
- "8541:8541"
postgres_alpha:
image: postgres:12-alpine
environment:
@@ -58,17 +67,24 @@ services:
volumes:
- ./volumes/postgres_alpha:/var/lib/postgresql/data

lemmy-beta-ui:
image: dessalines/lemmy-ui:v0.0.14
environment:
- LEMMY_INTERNAL_HOST=lemmy-beta:8551
- LEMMY_EXTERNAL_HOST=localhost:8551
- LEMMY_HTTPS=false
depends_on:
- lemmy-beta
lemmy-beta:
image: lemmy-federation:latest
environment:
- LEMMY_HOSTNAME=lemmy-beta:8550
- LEMMY_HOSTNAME=lemmy-beta:8551
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_beta:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8550
- LEMMY_PORT=8551
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-beta
@@ -79,6 +95,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_beta
ports:
- "8551:8551"
postgres_beta:
image: postgres:12-alpine
environment:
@@ -88,17 +106,24 @@ services:
volumes:
- ./volumes/postgres_beta:/var/lib/postgresql/data

lemmy-gamma-ui:
image: dessalines/lemmy-ui:v0.0.14
environment:
- LEMMY_INTERNAL_HOST=lemmy-gamma:8561
- LEMMY_EXTERNAL_HOST=localhost:8561
- LEMMY_HTTPS=false
depends_on:
- lemmy-gamma
lemmy-gamma:
image: lemmy-federation:latest
environment:
- LEMMY_HOSTNAME=lemmy-gamma:8560
- LEMMY_HOSTNAME=lemmy-gamma:8561
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_gamma:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8560
- LEMMY_PORT=8561
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
@@ -109,6 +134,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_gamma
ports:
- "8561:8561"
postgres_gamma:
image: postgres:12-alpine
environment:
@@ -119,17 +146,24 @@ services:
- ./volumes/postgres_gamma:/var/lib/postgresql/data

# An instance with only an allowlist for beta
lemmy-delta-ui:
image: dessalines/lemmy-ui:v0.0.14
environment:
- LEMMY_INTERNAL_HOST=lemmy-delta:8571
- LEMMY_EXTERNAL_HOST=localhost:8571
- LEMMY_HTTPS=false
depends_on:
- lemmy-delta
lemmy-delta:
image: lemmy-federation:latest
environment:
- LEMMY_HOSTNAME=lemmy-delta:8570
- LEMMY_HOSTNAME=lemmy-delta:8571
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_delta:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta
- LEMMY_PORT=8570
- LEMMY_PORT=8571
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-delta
@@ -140,6 +174,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_delta
ports:
- "8571:8571"
postgres_delta:
image: postgres:12-alpine
environment:
@@ -150,17 +186,24 @@ services:
- ./volumes/postgres_delta:/var/lib/postgresql/data

# An instance who has a blocklist, with lemmy-alpha blocked
lemmy-epsilon-ui:
image: dessalines/lemmy-ui:v0.0.14
environment:
- LEMMY_INTERNAL_HOST=lemmy-epsilon:8581
- LEMMY_EXTERNAL_HOST=localhost:8581
- LEMMY_HTTPS=false
depends_on:
- lemmy-epsilon
lemmy-epsilon:
image: lemmy-federation:latest
environment:
- LEMMY_HOSTNAME=lemmy-epsilon:8580
- LEMMY_HOSTNAME=lemmy-epsilon:8581
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_epsilon:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha
- LEMMY_PORT=8580
- LEMMY_PORT=8581
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-epsilon
@@ -171,6 +214,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_epsilon
ports:
- "8581:8581"
postgres_epsilon:
image: postgres:12-alpine
environment:


+ 35
- 35
docker/federation/nginx.conf View File

@@ -11,21 +11,21 @@ http {
# Upload limit for pictshare
client_max_body_size 50M;

location /api/v1 {
proxy_pass http://lemmy-alpha:8541/api/v1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://lemmy-alpha:8540;
proxy_pass http://lemmy-alpha-ui:1234;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Cuts off the trailing slash on URLs to make them valid
rewrite ^(.+)/+$ $1 permanent;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location /iframely/ {
proxy_pass http://iframely:80/;
proxy_set_header X-Real-IP $remote_addr;
@@ -42,21 +42,21 @@ http {
# Upload limit for pictshare
client_max_body_size 50M;

location /api/v1 {
proxy_pass http://lemmy-beta:8551/api/v1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://lemmy-beta:8550;
proxy_pass http://lemmy-beta-ui:1234;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Cuts off the trailing slash on URLs to make them valid
rewrite ^(.+)/+$ $1 permanent;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location /iframely/ {
proxy_pass http://iframely:80/;
proxy_set_header X-Real-IP $remote_addr;
@@ -73,21 +73,21 @@ http {
# Upload limit for pictshare
client_max_body_size 50M;

location /api/v1 {
proxy_pass http://lemmy-gamma:8561/api/v1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://lemmy-gamma:8560;
proxy_pass http://lemmy-gamma-ui:1234;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Cuts off the trailing slash on URLs to make them valid
rewrite ^(.+)/+$ $1 permanent;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location /iframely/ {
proxy_pass http://iframely:80/;
proxy_set_header X-Real-IP $remote_addr;
@@ -104,21 +104,21 @@ http {
# Upload limit for pictshare
client_max_body_size 50M;

location /api/v1 {
proxy_pass http://lemmy-delta:8571/api/v1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://lemmy-delta:8570;
proxy_pass http://lemmy-delta-ui:1234;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Cuts off the trailing slash on URLs to make them valid
rewrite ^(.+)/+$ $1 permanent;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location /iframely/ {
proxy_pass http://iframely:80/;
proxy_set_header X-Real-IP $remote_addr;
@@ -135,21 +135,21 @@ http {
# Upload limit for pictshare
client_max_body_size 50M;

location /api/v1 {
proxy_pass http://lemmy-epsilon:8581/api/v1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://lemmy-epsilon:8580;
proxy_pass http://lemmy-epsilon-ui:1234;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Cuts off the trailing slash on URLs to make them valid
rewrite ^(.+)/+$ $1 permanent;

# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location /iframely/ {
proxy_pass http://iframely:80/;
proxy_set_header X-Real-IP $remote_addr;


+ 7
- 6
docker/federation/run-tests.sh View File

@@ -15,13 +15,14 @@ sudo chown -R 991:991 volumes/pictrs_alpha

sudo docker-compose up -d

pushd ../../ui
pushd ../../api_tests
echo "Waiting for Lemmy to start..."
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8540/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8550/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8560/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8570/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8580/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8551/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8561/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8571/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8581/api/v1/site')" != "200" ]]; do sleep 1; done
yarn
yarn api-test || true
popd



+ 0
- 2
docker/lemmy.hjson View File

@@ -30,8 +30,6 @@
# host where postgres is running
host: "postgres"
}
# The location of the frontend
front_end_dir: "/app/dist"
# # optional: email sending configuration
# email: {
# # hostname of the smtp server


+ 9
- 22
docker/prod/Dockerfile View File

@@ -10,17 +10,17 @@ WORKDIR /app
RUN sudo chown -R rust:rust .
RUN USER=root cargo new server
WORKDIR /app/server
COPY server/Cargo.toml server/Cargo.lock ./
COPY server/lemmy_db ./lemmy_db
COPY server/lemmy_utils ./lemmy_utils
COPY server/lemmy_api_structs ./lemmy_api_structs
COPY server/lemmy_rate_limit ./lemmy_rate_limit
COPY Cargo.toml Cargo.lock ./
COPY lemmy_db ./lemmy_db
COPY lemmy_utils ./lemmy_utils
COPY lemmy_api_structs ./lemmy_api_structs
COPY lemmy_rate_limit ./lemmy_rate_limit
RUN mkdir -p ./src/bin \
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
RUN cargo build --release
RUN find target/$CARGO_BUILD_TARGET/$RUSTRELEASEDIR -type f -name "$(echo "lemmy_server" | tr '-' '_')*" -exec touch -t 200001010000 {} +
COPY server/src ./src/
COPY server/migrations ./migrations/
COPY src ./src/
COPY migrations ./migrations/

# build for release
# workaround for https://github.com/rust-lang/rust/issues/62896
@@ -36,18 +36,6 @@ WORKDIR /app
COPY --chown=rust:rust docs ./docs
RUN mdbook build docs/

FROM node:12-buster as node

WORKDIR /app/ui

# Cache deps
COPY ui/package.json ui/yarn.lock ./
RUN yarn install --pure-lockfile --network-timeout 600000

# Build
COPY ui /app/ui
RUN yarn build

FROM alpine:3.12 as lemmy

# Install libpq for postgres
@@ -60,10 +48,9 @@ RUN addgroup -g 1000 lemmy
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy

# Copy resources
COPY --chown=lemmy:lemmy server/config/defaults.hjson /config/defaults.hjson
COPY --chown=lemmy:lemmy config/defaults.hjson /config/defaults.hjson
COPY --chown=lemmy:lemmy --from=rust /app/server/lemmy_server /app/lemmy
COPY --chown=lemmy:lemmy --from=docs /app/docs/book/ /app/dist/documentation/
COPY --chown=lemmy:lemmy --from=node /app/ui/dist /app/dist
COPY --chown=lemmy:lemmy --from=docs /app/docs/book/ /app/documentation/

RUN chown lemmy:lemmy /app/lemmy
USER lemmy


+ 2
- 2
docker/prod/deploy.sh View File

@@ -13,8 +13,8 @@ third_semver=$(echo $new_tag | cut -d "." -f 3)
# Setting the version on the front end
cd ../../
# Setting the version on the backend
echo "pub const VERSION: &str = \"$new_tag\";" > "server/src/version.rs"
git add "server/src/version.rs"
echo "pub const VERSION: &str = \"$new_tag\";" > "src/version.rs"
git add "src/version.rs"
# Setting the version for Ansible
echo $new_tag > "ansible/VERSION"
git add "ansible/VERSION"


+ 11
- 0
docker/prod/docker-compose.yml View File

@@ -25,6 +25,17 @@ services:
- pictrs
- iframely

lemmy-ui:
image: dessalines/lemmy-ui:v0.0.14
ports:
- "1235:1234"
environment:
- LEMMY_INTERNAL_HOST=lemmy:8536
- LEMMY_EXTERNAL_HOST=localhost:8536
- LEMMY_HTTPS=false
depends_on:
- lemmy

pictrs:
image: asonix/pictrs:v0.1.13-r0
ports:


+ 20
- 49
docker/travis/docker-compose.yml View File

@@ -1,45 +1,16 @@
version: '3.3'

services:
nginx:
image: nginx:1.17-alpine
ports:
- "8540:8540"
- "8550:8550"
- "8560:8560"
- "8570:8570"
- "8580:8580"
volumes:
# Hack to make this work from both docker/federation/ and docker/federation-test/
- ../federation/nginx.conf:/etc/nginx/nginx.conf
restart: on-failure
depends_on:
- pictrs
- iframely
- lemmy-alpha
- lemmy-beta
- lemmy-gamma
- lemmy-delta
- lemmy-epsilon

pictrs:
restart: always
image: asonix/pictrs:v0.1.13-r0
user: 991:991
volumes:
- ./volumes/pictrs_alpha:/mnt

lemmy-alpha:
image: dessalines/lemmy:travis
environment:
- LEMMY_HOSTNAME=lemmy-alpha:8540
- LEMMY_HOSTNAME=lemmy-alpha:8541
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_alpha:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta,lemmy-gamma,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8540
- LEMMY_PORT=8541
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
@@ -50,6 +21,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_alpha
ports:
- "8541:8541"
postgres_alpha:
image: postgres:12-alpine
environment:
@@ -62,14 +35,13 @@ services:
lemmy-beta:
image: dessalines/lemmy:travis
environment:
- LEMMY_HOSTNAME=lemmy-beta:8550
- LEMMY_HOSTNAME=lemmy-beta:8551
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_beta:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-gamma,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8550
- LEMMY_PORT=8551
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-beta
@@ -80,6 +52,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_beta
ports:
- "8551:8551"
postgres_beta:
image: postgres:12-alpine
environment:
@@ -92,14 +66,13 @@ services:
lemmy-gamma:
image: dessalines/lemmy:travis
environment:
- LEMMY_HOSTNAME=lemmy-gamma:8560
- LEMMY_HOSTNAME=lemmy-gamma:8561
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_gamma:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-alpha,lemmy-beta,lemmy-delta,lemmy-epsilon
- LEMMY_PORT=8560
- LEMMY_PORT=8561
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
@@ -110,6 +83,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_gamma
ports:
- "8561:8561"
postgres_gamma:
image: postgres:12-alpine
environment:
@@ -123,14 +98,13 @@ services:
lemmy-delta:
image: dessalines/lemmy:travis
environment:
- LEMMY_HOSTNAME=lemmy-delta:8570
- LEMMY_HOSTNAME=lemmy-delta:8571
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_delta:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__ALLOWED_INSTANCES=lemmy-beta
- LEMMY_PORT=8570
- LEMMY_PORT=8571
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_delta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-delta
@@ -141,6 +115,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_delta
ports:
- "8571:8571"
postgres_delta:
image: postgres:12-alpine
environment:
@@ -154,14 +130,13 @@ services:
lemmy-epsilon:
image: dessalines/lemmy:travis
environment:
- LEMMY_HOSTNAME=lemmy-epsilon:8580
- LEMMY_HOSTNAME=lemmy-epsilon:8581
- LEMMY_DATABASE_URL=postgres://lemmy:password@postgres_epsilon:5432/lemmy
- LEMMY_JWT_SECRET=changeme
- LEMMY_FRONT_END_DIR=/app/dist
- LEMMY_FEDERATION__ENABLED=true
- LEMMY_FEDERATION__TLS_ENABLED=false
- LEMMY_FEDERATION__BLOCKED_INSTANCES=lemmy-alpha
- LEMMY_PORT=8580
- LEMMY_PORT=8581
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_epsilon
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-epsilon
@@ -172,6 +147,8 @@ services:
- RUST_LOG=debug
depends_on:
- postgres_epsilon
ports:
- "8581:8581"
postgres_epsilon:
image: postgres:12-alpine
environment:
@@ -180,9 +157,3 @@ services:
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres_epsilon:/var/lib/postgresql/data

iframely:
image: dogbin/iframely:latest
volumes:
- ../iframely.config.local.js:/iframely/config.local.js:ro
restart: always

+ 6
- 6
docker/travis/run-tests.sh View File

@@ -12,13 +12,13 @@ sudo docker build ../../ --file ../prod/Dockerfile --tag dessalines/lemmy:travis

sudo docker-compose up -d

pushd ../../ui
pushd ../../api_tests
echo "Waiting for Lemmy to start..."
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8540/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8550/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8560/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8570/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8580/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8541/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8551/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8561/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8571/api/v1/site')" != "200" ]]; do sleep 1; done
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8581/api/v1/site')" != "200" ]]; do sleep 1; done
yarn
yarn api-test
popd


+ 1
- 0
docs/src/about_features.md View File

@@ -31,4 +31,5 @@
- High performance.
- Server is written in rust.
- Front end is `~80kB` gzipped.
- Front end works without javascript (read-only).
- Supports arm64 / Raspberry Pi.

+ 1
- 1
docs/src/administration_configuration.md View File

@@ -1,7 +1,7 @@
# Configuration

The configuration is based on the file
[defaults.hjson](https://yerbamate.dev/LemmyNet/lemmy/src/branch/main/server/config/defaults.hjson).
[defaults.hjson](https://yerbamate.dev/LemmyNet/lemmy/src/branch/main/config/defaults.hjson).
This file also contains documentation for all the available options. To override the defaults, you
can copy the options you want to change into your local `config.hjson` file.



+ 4
- 3
docs/src/contributing.md View File

@@ -18,13 +18,14 @@ Check out [Lemmy's Weblate](https://weblate.yerbamate.dev/projects/lemmy/) for t
### Front end

- The front end is written in `typescript`, using a react-like framework called [inferno](https://infernojs.org/). All UI elements are reusable `.tsx` components.
- The main page and routing are in `ui/src/index.tsx`.
- The components are located in `ui/src/components`.
- The front end repository is [lemmy-ui](https://github.com/LemmyNet/lemmy-ui).
- The routes are at `src/shared/routes.ts`.
- The components are located in `src/shared/components`.

### Back end

- The back end is written in `rust`, using `diesel`, and `actix`.
- The server source code is split into main sections in `server/src`. These include:
- The server source code is split into main sections in `src`. These include:
- `db` - The low level database actions.
- Database additions are done using diesel migrations. Run `diesel migration generate xxxxx` to add new things.
- `api` - The high level user interactions (things like `CreateComment`)


+ 3
- 1
docs/src/contributing_docker_development.md View File

@@ -9,7 +9,9 @@ cd lemmy/docker/dev
sudo docker-compose up --no-deps --build
```

and go to http://localhost:8536.
and go to http://localhost:1235.

*Note: many features (like docs and pictures) will not work without using an nginx profile like that in `ansible/templates/nginx.conf`.

To speed up the Docker compile, add the following to `/etc/docker/daemon.json` and restart Docker.
```


+ 15
- 20
docs/src/contributing_local_development.md View File

@@ -20,28 +20,22 @@ Finally, install Node and Yarn.
brew install node yarn
```

### Get the source code
### Get the back end source code
```
git clone https://github.com/LemmyNet/lemmy.git
# or alternatively from gitea
# git clone https://yerbamate.dev/LemmyNet/lemmy.git
```

All the following commands need to be run either in `lemmy/server` or `lemmy/ui`, as indicated
by the `cd` command.

### Build the backend (Rust)
```
cd server
cargo build
# for development, use `cargo check` instead)
```

### Build the frontend (Typescript)
### Get the front end source code
```
cd ui
yarn
yarn build
git clone https://github.com/LemmyNet/lemmy-ui.git
```

### Setup postgresql
@@ -50,7 +44,7 @@ yarn build
sudo apt install postgresql
sudo systemctl start postgresql

# Either execute server/db-init.sh, or manually initialize the postgres database:
# Either execute db-init.sh, or manually initialize the postgres database:
sudo -u postgres psql -c "create user lemmy with password 'password' superuser;" -U postgres
sudo -u postgres psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
@@ -62,7 +56,7 @@ brew install postgresql
brew services start postgresql
/usr/local/opt/postgres/bin/createuser -s postgres

# Either execute server/db-init.sh, or manually initialize the postgres database:
# Either execute db-init.sh, or manually initialize the postgres database:
psql -c "create user lemmy with password 'password' superuser;" -U postgres
psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
@@ -70,21 +64,22 @@ export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy

### Run a local development instance
```
# run each of these in a seperate terminal
cd server && cargo run
cd ui && yarn start
cd lemmy
cargo run
```

Then open [localhost:4444](http://localhost:4444) in your browser. It will auto-refresh if you edit
any frontend files. For backend coding, you will have to rerun `cargo run`. You can use
`cargo check` as a faster way to find compilation errors.
Then open [localhost:1235](http://localhost:1235) in your browser. To reload back-end changes, you will have to rerun `cargo run`. You can use `cargo check` as a faster way to find compilation errors.
To do front end development:

To speed up incremental builds, you can add the following to `~/.cargo/config`:
```
[target.x86_64-unknown-linux-gnu]
rustflags = ["-Clink-arg=-fuse-ld=lld"]
cd lemmy-ui
yarn
yarn dev
```

and goto [localhost:1234](http://localhost:1234). Front end saves should rebuild the project.

Note that this setup doesn't include image uploads or link previews (provided by pict-rs and
iframely respectively). If you want to test those, you should use the
[Docker development](contributing_docker_development.md).

+ 1
- 1
docs/src/contributing_tests.md View File

@@ -3,7 +3,7 @@
#### Rust

After installing [local development dependencies](contributing_local_development.md), run the
following commands in the `server` subfolder:
following commands:

```bash
psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"


+ 3
- 2
docs/src/contributing_theming.md View File

@@ -12,7 +12,8 @@ Lemmy uses [Bootstrap v4](https://getbootstrap.com/), and very few custom css cl

## Adding

1. Copy `{my-theme-name}.min.css` to `ui/assets/css/themes`. (You can also copy the `_variables.scss` here if you want).
1. Go to `ui/src/utils.ts` and add `{my-theme-name}` to the themes list.
1. Fork the [lemmy-ui](https://github.com/LemmyNet/lemmy-ui).
1. Copy `{my-theme-name}.min.css` to `src/assets/css/themes`. (You can also copy the `_variables.scss` here if you want).
1. Go to `src/shared/utils.ts` and add `{my-theme-name}` to the themes list.
1. Test locally
1. Do a pull request with those changes.

+ 187
- 57
docs/src/contributing_websocket_http_api.md View File

@@ -1,6 +1,6 @@
# Lemmy API

*Note: this may lag behind the actual API endpoints [here](../server/src/api). The API should be considered unstable and may change any time.*
*Note: this may lag behind the actual API endpoints [here](../src/api). The API should be considered unstable and may change any time.*

<!-- toc -->

@@ -28,214 +28,234 @@
- [Request](#request-1)
- [Response](#response-1)
- [HTTP](#http-2)
+ [Get User Details](#get-user-details)
+ [Get Captcha](#get-captcha)
- [Request](#request-2)
- [Response](#response-2)
- [HTTP](#http-3)
+ [Save User Settings](#save-user-settings)
+ [Get User Details](#get-user-details)
- [Request](#request-3)
- [Response](#response-3)
- [HTTP](#http-4)
+ [Get Replies / Inbox](#get-replies--inbox)
+ [Save User Settings](#save-user-settings)
- [Request](#request-4)
- [Response](#response-4)
- [HTTP](#http-5)
+ [Get User Mentions](#get-user-mentions)
+ [Get Replies / Inbox](#get-replies--inbox)
- [Request](#request-5)
- [Response](#response-5)
- [HTTP](#http-6)
+ [Mark User Mention as read](#mark-user-mention-as-read)
+ [Get User Mentions](#get-user-mentions)
- [Request](#request-6)
- [Response](#response-6)
- [HTTP](#http-7)
+ [Get Private Messages](#get-private-messages)
+ [Mark User Mention as read](#mark-user-mention-as-read)
- [Request](#request-7)
- [Response](#response-7)
- [HTTP](#http-8)
+ [Create Private Message](#create-private-message)
+ [Get Private Messages](#get-private-messages)
- [Request](#request-8)
- [Response](#response-8)
- [HTTP](#http-9)
+ [Edit Private Message](#edit-private-message)
+ [Create Private Message](#create-private-message)
- [Request](#request-9)
- [Response](#response-9)
- [HTTP](#http-10)
+ [Delete Private Message](#delete-private-message)
+ [Edit Private Message](#edit-private-message)
- [Request](#request-10)
- [Response](#response-10)
- [HTTP](#http-11)
+ [Mark Private Message as Read](#mark-private-message-as-read)
+ [Delete Private Message](#delete-private-message)
- [Request](#request-11)
- [Response](#response-11)
- [HTTP](#http-12)
+ [Mark All As Read](#mark-all-as-read)
+ [Mark Private Message as Read](#mark-private-message-as-read)
- [Request](#request-12)
- [Response](#response-12)
- [HTTP](#http-13)
+ [Delete Account](#delete-account)
+ [Mark All As Read](#mark-all-as-read)
- [Request](#request-13)
- [Response](#response-13)
- [HTTP](#http-14)
+ [Add admin](#add-admin)
+ [Delete Account](#delete-account)
- [Request](#request-14)
- [Response](#response-14)
- [HTTP](#http-15)
+ [Ban user](#ban-user)
+ [Add admin](#add-admin)
- [Request](#request-15)
- [Response](#response-15)
- [HTTP](#http-16)
* [Site](#site)
+ [List Categories](#list-categories)
+ [Ban user](#ban-user)
- [Request](#request-16)
- [Response](#response-16)
- [HTTP](#http-17)
+ [Search](#search)
+ [User Join](#user-join)
- [Request](#request-17)
- [Response](#response-17)
- [HTTP](#http-18)
+ [Get Modlog](#get-modlog)
* [Site](#site)
+ [List Categories](#list-categories)
- [Request](#request-18)
- [Response](#response-18)
- [HTTP](#http-19)
+ [Create Site](#create-site)
+ [Search](#search)
- [Request](#request-19)
- [Response](#response-19)
- [HTTP](#http-20)
+ [Edit Site](#edit-site)
+ [Get Modlog](#get-modlog)
- [Request](#request-20)
- [Response](#response-20)
- [HTTP](#http-21)
+ [Get Site](#get-site)
+ [Create Site](#create-site)
- [Request](#request-21)
- [Response](#response-21)
- [HTTP](#http-22)
+ [Transfer Site](#transfer-site)
+ [Edit Site](#edit-site)
- [Request](#request-22)
- [Response](#response-22)
- [HTTP](#http-23)
+ [Get Site Config](#get-site-config)
+ [Get Site](#get-site)
- [Request](#request-23)
- [Response](#response-23)
- [HTTP](#http-24)
+ [Save Site Config](#save-site-config)
+ [Transfer Site](#transfer-site)
- [Request](#request-24)
- [Response](#response-24)
- [HTTP](#http-25)
* [Community](#community)
+ [Get Community](#get-community)
+ [Get Site Config](#get-site-config)
- [Request](#request-25)
- [Response](#response-25)
- [HTTP](#http-26)
+ [Create Community](#create-community)
+ [Save Site Config](#save-site-config)
- [Request](#request-26)
- [Response](#response-26)
- [HTTP](#http-27)
+ [List Communities](#list-communities)
* [Community](#community)
+ [Get Community](#get-community)
- [Request](#request-27)
- [Response](#response-27)
- [HTTP](#http-28)
+ [Ban from Community](#ban-from-community)
+ [Create Community](#create-community)
- [Request](#request-28)
- [Response](#response-28)
- [HTTP](#http-29)
+ [Add Mod to Community](#add-mod-to-community)
+ [List Communities](#list-communities)
- [Request](#request-29)
- [Response](#response-29)
- [HTTP](#http-30)
+ [Edit Community](#edit-community)
+ [Ban from Community](#ban-from-community)
- [Request](#request-30)
- [Response](#response-30)
- [HTTP](#http-31)
+ [Delete Community](#delete-community)
+ [Add Mod to Community](#add-mod-to-community)
- [Request](#request-31)
- [Response](#response-31)
- [HTTP](#http-32)
+ [Remove Community](#remove-community)
+ [Edit Community](#edit-community)
- [Request](#request-32)
- [Response](#response-32)
- [HTTP](#http-33)
+ [Follow Community](#follow-community)
+ [Delete Community](#delete-community)
- [Request](#request-33)
- [Response](#response-33)
- [HTTP](#http-34)
+ [Get Followed Communities](#get-followed-communities)
+ [Remove Community](#remove-community)
- [Request](#request-34)
- [Response](#response-34)
- [HTTP](#http-35)
+ [Transfer Community](#transfer-community)
+ [Follow Community](#follow-community)
- [Request](#request-35)
- [Response](#response-35)
- [HTTP](#http-36)
* [Post](#post)
+ [Create Post](#create-post)
+ [Get Followed Communities](#get-followed-communities)
- [Request](#request-36)
- [Response](#response-36)
- [HTTP](#http-37)
+ [Get Post](#get-post)
+ [Transfer Community](#transfer-community)
- [Request](#request-37)
- [Response](#response-37)
- [HTTP](#http-38)
+ [Get Posts](#get-posts)
+ [Community Join](#community-join)
- [Request](#request-38)
- [Response](#response-38)
- [HTTP](#http-39)
+ [Create Post Like](#create-post-like)
* [Post](#post)
+ [Create Post](#create-post)
- [Request](#request-39)
- [Response](#response-39)
- [HTTP](#http-40)
+ [Edit Post](#edit-post)
+ [Get Post](#get-post)
- [Request](#request-40)
- [Response](#response-40)
- [HTTP](#http-41)
+ [Delete Post](#delete-post)
+ [Get Posts](#get-posts)
- [Request](#request-41)
- [Response](#response-41)
- [HTTP](#http-42)
+ [Remove Post](#remove-post)
+ [Create Post Like](#create-post-like)
- [Request](#request-42)
- [Response](#response-42)
- [HTTP](#http-43)
+ [Lock Post](#lock-post)
+ [Edit Post](#edit-post)
- [Request](#request-43)
- [Response](#response-43)
- [HTTP](#http-44)
+ [Sticky Post](#sticky-post)
+ [Delete Post](#delete-post)
- [Request](#request-44)
- [Response](#response-44)
- [HTTP](#http-45)
+ [Save Post](#save-post)
+ [Remove Post](#remove-post)
- [Request](#request-45)
- [Response](#response-45)
- [HTTP](#http-46)
* [Comment](#comment)
+ [Create Comment](#create-comment)
+ [Lock Post](#lock-post)
- [Request](#request-46)
- [Response](#response-46)
- [HTTP](#http-47)
+ [Edit Comment](#edit-comment)
+ [Sticky Post](#sticky-post)
- [Request](#request-47)
- [Response](#response-47)
- [HTTP](#http-48)
+ [Delete Comment](#delete-comment)
+ [Save Post](#save-post)
- [Request](#request-48)
- [Response](#response-48)
- [HTTP](#http-49)
+ [Remove Comment](#remove-comment)
+ [Post Join](#post-join)
- [Request](#request-49)
- [Response](#response-49)
- [HTTP](#http-50)
+ [Mark Comment as Read](#mark-comment-as-read)
* [Comment](#comment)
+ [Create Comment](#create-comment)
- [Request](#request-50)
- [Response](#response-50)
- [HTTP](#http-51)
+ [Save Comment](#save-comment)
+ [Edit Comment](#edit-comment)
- [Request](#request-51)
- [Response](#response-51)
- [HTTP](#http-52)
+ [Create Comment Like](#create-comment-like)
+ [Delete Comment](#delete-comment)
- [Request](#request-52)
- [Response](#response-52)
- [HTTP](#http-53)
+ [Remove Comment](#remove-comment)
- [Request](#request-53)
- [Response](#response-53)
- [HTTP](#http-54)
+ [Get Comments](#get-comments)
- [Request](#request-54)
- [Response](#response-54)
- [HTTP](#http-55)
+ [Mark Comment as Read](#mark-comment-as-read)
- [Request](#request-55)
- [Response](#response-55)
- [HTTP](#http-56)
+ [Save Comment](#save-comment)
- [Request](#request-56)
- [Response](#response-56)
- [HTTP](#http-57)
+ [Create Comment Like](#create-comment-like)
- [Request](#request-57)
- [Response](#response-57)
- [HTTP](#http-58)
* [RSS / Atom feeds](#rss--atom-feeds)
+ [All](#all)
+ [Community](#community-1)
@@ -249,7 +269,7 @@
- <code>Option<***SomeType***></code> designates an option which may be omitted in requests and not be present in responses. It will be of type ***SomeType***.
- <code>Vec<***SomeType***></code> is a list which contains objects of type ***SomeType***.
- `chrono::NaiveDateTime` is a timestamp string in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Timestamps will be UTC.
- Other data types are listed [here](../server/src/db).
- Other data types are listed [here](../src/db).

## Basic usage

@@ -261,6 +281,12 @@ 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>.

To receive websocket messages, you must join a room / context. The three available are:

- [UserJoin](#user-join). Receives replies, private messages, etc.
- [PostJoin](#post-join). Receives new comments on a post.
- [CommunityJoin](#community-join). Receives front page / community posts.

#### Testing with Websocat

[Websocat link](https://github.com/vi/websocat)
@@ -839,6 +865,29 @@ Marks all user replies and mentions as read.

`POST /user/ban`

#### User Join
##### Request
```rust
{
op: "UserJoin",
data: {
auth: String
}
}
```
##### Response
```rust
{
op: "UserJoin",
data: {
joined: bool,
}
}
```
##### HTTP

`POST /user/join`

### Site
#### List Categories
##### Request
@@ -1387,6 +1436,32 @@ Only admins can remove a community.

`POST /community/transfer`

#### Community Join

The main / frontpage community is `community_id: 0`.

##### Request
```rust
{
op: "CommunityJoin",
data: {
community_id: i32
}
}
```
##### Response
```rust
{
op: "CommunityJoin",
data: {
joined: bool,
}
}
```
##### HTTP

`POST /community/join`

### Post
#### Create Post
##### Request
@@ -1671,6 +1746,29 @@ Only admins and mods can sticky a post.

`POST /post/save`

#### Post Join
##### Request
```rust
{
op: "PostJoin",
data: {
post_id: i32
}
}
```
##### Response
```rust
{
op: "PostJoin",
data: {
joined: bool,
}
}
```
##### HTTP

`POST /post/join`

### Comment
#### Create Comment
##### Request
@@ -1787,6 +1885,38 @@ Only a mod or admin can remove the comment.

`POST /comment/remove`

#### Get Comments

Comment listing types are `All, Subscribed, Community`

##### Request
```rust
{
op: "GetComments",
data: {
type_: String,
sort: String,
page: Option<i64>,
limit: Option<i64>,
community_id: Option<i32>,
community_name: Option<String>,
auth: Option<String>
}
}
```
##### Response
```rust
{
op: "GetComments",
data: {
comments: Vec<CommentView>,
}
}
```
##### HTTP

`GET /comment/list`

#### Mark Comment as Read

Only the recipient can do this.


+ 1
- 1
install.sh View File

@@ -29,7 +29,7 @@ ask_to_init_db() {
done
if [ "$init_db_final" = 1 ]
then
source ./server/db-init.sh
source ./db-init.sh
read -n 1 -s -r -p "Press ANY KEY to continue execution of this script, press CTRL+C to quit..."
echo
fi


server/lemmy_api_structs/Cargo.toml → lemmy_api_structs/Cargo.toml View File


server/lemmy_api_structs/src/comment.rs → lemmy_api_structs/src/comment.rs View File

@@ -68,6 +68,7 @@ pub struct GetComments {
pub page: Option<i64>,
pub limit: Option<i64>,
pub community_id: Option<i32>,
pub community_name: Option<String>,
pub auth: Option<String>,
}


server/lemmy_api_structs/src/community.rs → lemmy_api_structs/src/community.rs View File

@@ -129,3 +129,13 @@ pub struct TransferCommunity {
pub user_id: i32,
pub auth: String,
}

#[derive(Deserialize, Debug)]
pub struct CommunityJoin {
pub community_id: i32,
}

#[derive(Serialize, Clone)]
pub struct CommunityJoinResponse {
pub joined: bool,
}

server/lemmy_api_structs/src/lib.rs → lemmy_api_structs/src/lib.rs View File

@@ -1,7 +1,7 @@
extern crate serde;
extern crate log;
extern crate diesel;
extern crate actix_web;
extern crate diesel;
extern crate log;
extern crate serde;

pub mod comment;
pub mod community;
@@ -9,22 +9,22 @@ pub mod post;
pub mod site;
pub mod user;

use lemmy_db::comment::Comment;
use lemmy_db::user::User_;
use lemmy_db::post::Post;
use lemmy_db::user_mention::{UserMentionForm, UserMention};
use log::error;
use lemmy_db::{Crud, DbPool};
use lemmy_utils::utils::MentionData;
use lemmy_utils::settings::Settings;
use lemmy_utils::email::send_email;
use diesel::PgConnection;
use lemmy_utils::LemmyError;
use lemmy_db::{
comment::Comment,
post::Post,
user::User_,
user_mention::{UserMention, UserMentionForm},
Crud,
DbPool,
};
use lemmy_utils::{email::send_email, settings::Settings, utils::MentionData, LemmyError};
use log::error;

pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
where
F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
T: Send + 'static,
where
F: FnOnce(&diesel::PgConnection) -> T + Send + 'static,
T: Send + 'static,
{
let pool = pool.clone();
let res = actix_web::web::block(move || {
@@ -32,7 +32,7 @@ pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
let res = (f)(&conn);
Ok(res) as Result<_, LemmyError>
})
.await?;
.await?;

Ok(res)
}
@@ -49,7 +49,7 @@ pub async fn send_local_notifs(
let ids = blocking(pool, move |conn| {
do_send_local_notifs(conn, &mentions, &comment, &user2, &post, do_send_email)
})
.await?;
.await?;

Ok(ids)
}

server/lemmy_api_structs/src/post.rs → lemmy_api_structs/src/post.rs View File

@@ -103,3 +103,13 @@ pub struct SavePost {
pub save: bool,
pub auth: String,
}

#[derive(Deserialize, Debug)]
pub struct PostJoin {
pub post_id: i32,
}

#[derive(Serialize, Clone)]
pub struct PostJoinResponse {
pub joined: bool,
}

server/lemmy_api_structs/src/site.rs → lemmy_api_structs/src/site.rs View File


server/lemmy_api_structs/src/user.rs → lemmy_api_structs/src/user.rs View File

@@ -235,5 +235,5 @@ pub struct UserJoin {

#[derive(Serialize, Clone)]
pub struct UserJoinResponse {
pub user_id: i32,
pub joined: bool,
}

server/lemmy_db/Cargo.toml → lemmy_db/Cargo.toml View File


server/lemmy_db/src/activity.rs → lemmy_db/src/activity.rs View File


server/lemmy_db/src/category.rs → lemmy_db/src/category.rs