Browse Source

Merge pull request 'Selfhosted frontend' (#191) from feature/frontend-selfhost into main

Reviewed-on: https://git.chapo.chat/hexbear-collective/hexbear-frontend/pulls/191
pull/200/head v0.8.0.hex
DashEightMate 1 month ago
parent
commit
7dea9b3eef
  1. 1
      .eslintignore
  2. 23
      docker/Dockerfile
  3. 1
      docs/.gitignore
  4. 6
      docs/book.toml
  5. BIN
      docs/img/chat_screen.png
  6. BIN
      docs/img/main_screen.png
  7. BIN
      docs/img/rank_algorithm.png
  8. 22
      docs/src/SUMMARY.md
  9. 30
      docs/src/about.md
  10. 34
      docs/src/about_features.md
  11. 54
      docs/src/about_goals.md
  12. 42
      docs/src/about_guide.md
  13. 31
      docs/src/about_ranking.md
  14. 3
      docs/src/administration.md
  15. 83
      docs/src/administration_backup_and_restore.md
  16. 24
      docs/src/administration_configuration.md
  17. 25
      docs/src/administration_install_ansible.md
  18. 41
      docs/src/administration_install_docker.md
  19. 24
      docs/src/administration_install_kubernetes.md
  20. 39
      docs/src/contributing.md
  21. 378
      docs/src/contributing_apub_api_outline.md
  22. 40
      docs/src/contributing_docker_development.md
  23. 88
      docs/src/contributing_federation_development.md
  24. 151
      docs/src/contributing_local_development.md
  25. 16
      docs/src/contributing_tests.md
  26. 18
      docs/src/contributing_theming.md
  27. 1884
      docs/src/contributing_websocket_http_api.md
  28. 79
      docs/src/lemmy_council.md
  29. 23
      index.html
  30. 6
      package.json
  31. 2
      server/.eslintignore
  32. 22
      server/.eslintrc.json
  33. 118
      server/.gitignore
  34. 678
      server/LICENSE
  35. 34
      server/package.json
  36. 70
      server/src/index.ts
  37. 119
      server/src/meta.ts
  38. 16
      server/tsconfig.json
  39. 2855
      server/yarn.lock
  40. 2
      src/tagline.tsx
  41. 24
      src/taglines.json

1
.eslintignore

@ -5,3 +5,4 @@ src/translations
jest.config.js
vite.config.ts
git-version.ts
server/

23
docker/Dockerfile

@ -12,6 +12,27 @@ COPY . /app/
COPY .git/ ./.git/
RUN yarn build
RUN su && rm -rf .git
VOLUME /app/dist
FROM ekidd/rust-musl-builder:latest as docs
WORKDIR /app
COPY docs ./docs
RUN sudo chown rust:rust ./docs
RUN mdbook build docs/
FROM node:14-alpine as server
RUN apk update && apk add yarn && rm -rf /var/cache/apk/*
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=docs /app/docs/book ./dist/documentation
WORKDIR /app/server
COPY server/ ./
RUN yarn install --pure-lockfile
EXPOSE 4444
CMD ["node", "-r", "ts-node/register", "src/index.ts"]

1
docs/.gitignore

@ -0,0 +1 @@
book

6
docs/book.toml

@ -0,0 +1,6 @@
[book]
authors = ["Felix Ableitner"]
language = "en"
multilingual = false
src = "src"
title = "Lemmy Documentation"

BIN
docs/img/chat_screen.png

After

Width: 1836  |  Height: 921  |  Size: 78 KiB

BIN
docs/img/main_screen.png

After

Width: 1839  |  Height: 921  |  Size: 92 KiB

BIN
docs/img/rank_algorithm.png

After

Width: 617  |  Height: 702  |  Size: 54 KiB

22
docs/src/SUMMARY.md

@ -0,0 +1,22 @@
# Summary
- [About](about.md)
- [Features](about_features.md)
- [Goals](about_goals.md)
- [Post and Comment Ranking](about_ranking.md)
- [Guide](about_guide.md)
- [Administration](administration.md)
- [Install with Docker](administration_install_docker.md)
- [Install with Ansible](administration_install_ansible.md)
- [Install with Kubernetes](administration_install_kubernetes.md)
- [Configuration](administration_configuration.md)
- [Backup and Restore](administration_backup_and_restore.md)
- [Contributing](contributing.md)
- [Docker Development](contributing_docker_development.md)
- [Local Development](contributing_local_development.md)
- [Tests](contributing_tests.md)
- [Federation Development](contributing_federation_development.md)
- [Websocket/HTTP API](contributing_websocket_http_api.md)
- [ActivityPub API Outline](contributing_apub_api_outline.md)
- [Theming Guide](contributing_theming.md)
- [Lemmy Council](lemmy_council.md)

30
docs/src/about.md

@ -0,0 +1,30 @@
## About The Project
Front Page|Post
---|---
![main screen](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/main_screen.png)|![chat screen](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/chat_screen.png)
[Lemmy](https://github.com/LemmyNet/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
For a link aggregator, this means a user registered on one server can subscribe to forums on any other server, and can have discussions with users registered elsewhere.
The overall goal is to create an easily self-hostable, decentralized alternative to reddit and other link aggregators, outside of their corporate control and meddling.
Each lemmy server can set its own moderation policy; appointing site-wide admins, and community moderators to keep out the trolls, and foster a healthy, non-toxic environment where all can feel comfortable contributing.
*Note: Federation is still in active development*
### Why's it called Lemmy?
- Lead singer from [Motörhead](https://invidio.us/watch?v=pWB5JZRGl0U).
- The old school [video game](<https://en.wikipedia.org/wiki/Lemmings_(video_game)>).
- The [Koopa from Super Mario](https://www.mariowiki.com/Lemmy_Koopa).
- The [furry rodents](http://sunchild.fpwc.org/lemming-the-little-giant-of-the-north/).
### Built With
- [Rust](https://www.rust-lang.org)
- [Actix](https://actix.rs/)
- [Diesel](http://diesel.rs/)
- [Inferno](https://infernojs.org)
- [Typescript](https://www.typescriptlang.org/)

34
docs/src/about_features.md

@ -0,0 +1,34 @@
# Features
- Open source, [AGPL License](/LICENSE).
- Self hostable, easy to deploy.
- Comes with [Docker](#docker), [Ansible](#ansible), [Kubernetes](#kubernetes).
- Clean, mobile-friendly interface.
- Only a minimum of a username and password is required to sign up!
- User avatar support.
- Live-updating Comment threads.
- Full vote scores `(+/-)` like old reddit.
- Themes, including light, dark, and solarized.
- Emojis with autocomplete support. Start typing `:`
- User tagging using `@`, Community tagging using `#`.
- Integrated image uploading in both posts and comments.
- A post can consist of a title and any combination of self text, a URL, or nothing else.
- Notifications, on comment replies and when you're tagged.
- Notifications can be sent via email.
- i18n / internationalization support.
- RSS / Atom feeds for `All`, `Subscribed`, `Inbox`, `User`, and `Community`.
- Cross-posting support.
- A *similar post search* when creating new posts. Great for question / answer communities.
- Moderation abilities.
- Public Moderation Logs.
- Can sticky posts to the top of communities.
- Both site admins, and community moderators, who can appoint other moderators.
- Can lock, remove, and restore posts and comments.
- Can ban and unban users from communities and the site.
- Can transfer site and communities to others.
- Can fully erase your data, replacing all posts and comments.
- NSFW post / community support.
- High performance.
- Server is written in rust.
- Front end is `~80kB` gzipped.
- Supports arm64 / Raspberry Pi.

54
docs/src/about_goals.md

@ -0,0 +1,54 @@
# Goals
- Come up with a name / codename.
- Must have communities.
- Must have threaded comments.
- Must be federated: liking and following communities across instances.
- Be live-updating: have a right pane for new comments, and a main pain for the full threaded view.
- Use websockets for post / gets to your own instance.
# Questions
- How does voting work? Should we go back to the old way of showing up and downvote counts? Or just a score?
- Decide on tech to be used
- Backend: Actix, Diesel.
- Frontend: inferno, typescript and bootstrap for now.
- Should it allow bots?
- Should the comments / votes be static, or feel like a chat, like [flowchat?](https://flow-chat.com).
- Two pane model - Right pane is live comments, left pane is live tree view.
- On mobile, allow you to switch between them. Default?
# Resources / Potential Libraries
- [Diesel to Postgres data types](https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html)
- [helpful diesel examples](http://siciarz.net/24-days-rust-diesel/)
- [Recursive query for adjacency list for nested comments](https://stackoverflow.com/questions/192220/what-is-the-most-efficient-elegant-way-to-parse-a-flat-table-into-a-tree/192462#192462)
- https://github.com/sparksuite/simplemde-markdown-editor
- [Markdown-it](https://github.com/markdown-it/markdown-it)
- [Sticky Sidebar](https://stackoverflow.com/questions/38382043/how-to-use-css-position-sticky-to-keep-a-sidebar-visible-with-bootstrap-4/49111934)
- [RXJS websocket](https://stackoverflow.com/questions/44060315/reconnecting-a-websocket-in-angular-and-rxjs/44067972#44067972)
- [Rust JWT](https://github.com/Keats/jsonwebtoken)
- [Hierarchical tree building javascript](https://stackoverflow.com/a/40732240/1655478)
- [Hot sorting discussion](https://meta.stackexchange.com/questions/11602/what-formula-should-be-used-to-determine-hot-questions) [2](https://medium.com/hacking-and-gonzo/how-reddit-ranking-algorithms-work-ef111e33d0d9)
- [Classification types.](https://www.reddit.com/r/ModeratorDuck/wiki/subreddit_classification)
- [RES expando - Possibly make this into a switching react component.](https://github.com/honestbleeps/Reddit-Enhancement-Suite/tree/d21f55c21e734f47d8ed03fe0ebce5b16653b0bd/lib/modules/hosts)
- [Temp Icon](https://www.flaticon.com/free-icon/mouse_194242)
- [Rust docker build](https://shaneutt.com/blog/rust-fast-small-docker-image-builds/)
- [Zurb mentions](https://github.com/zurb/tribute)
- [TippyJS](https://github.com/atomiks/tippyjs)
## Activitypub guides
- https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
- https://raw.githubusercontent.com/w3c/activitypub/gh-pages/activitypub-tutorial.txt
- https://github.com/tOkeshu/activitypub-example
- https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/
- Use the [activitypub crate.](https://docs.rs/activitypub/0.1.4/activitypub/)
- https://docs.rs/activitypub/0.1.4/activitypub/
- [Activitypub vocab.](https://www.w3.org/TR/activitystreams-vocabulary/)
- [Activitypub main](https://www.w3.org/TR/activitypub/)
- [Federation.md](https://github.com/dariusk/gathio/blob/7fc93dbe9d4d99457a0e85c6c532112f415b7af2/FEDERATION.md)
- [Activitypub implementers guide](https://socialhub.activitypub.rocks/t/draft-guide-for-new-activitypub-implementers/479)
- [Data storage questions](https://socialhub.activitypub.rocks/t/data-storage-questions/579/3)
- [Activitypub as it has been understood](https://flak.tedunangst.com/post/ActivityPub-as-it-has-been-understood)
- [Asonix http signatures in rust](https://git.asonix.dog/Aardwolf/http-signature-normalization)

42
docs/src/about_guide.md

@ -0,0 +1,42 @@
# Lemmy Guide
Start typing...
- `@a_user_name` to get a list of usernames.
- `!a_community` to get a list of communities.
- `:emoji` to get a list of emojis.
## Sorting
_Applies to both posts and comments_
| Type | Description |
| ------ | ----------------------------------------------------------------------------- |
| Active | Shows _trending_ posts, based on the score, and the most recent comment time. |
| Hot | Shows highest ranking posts, but decays over time |
| New | Newest posts. |
| Top | Shows the highest scoring posts in the given time frame. |
For more detail, check the [Post and Comment Ranking details](about_ranking.md).
## Markdown Guide
Type | Or | … to Get
--- | --- | ---
\*Italic\* | \_Italic\_ | _Italic_
\*\*Bold\*\* | \_\_Bold\_\_ | **Bold**
\# Heading 1 | Heading 1 <br> ========= | <h4>Heading 1</h4>
\## Heading 2 | Heading 2 <br>--------- | <h5>Heading 2</h5>
\[Link\](http://a.com) | \[Link\]\[1\]<br><br>\[1\]: http://b.org | [Link](https://commonmark.org/)
!\[Image\](http://url/a.png) | !\[Image\]\[1\]<br><br>\[1\]: http://url/b.jpg | ![Markdown](https://commonmark.org/help/images/favicon.png)
\> Blockquote | | <blockquote>Blockquote</blockquote>
\* List <br>\* List <br>\* List | \- List <br>\- List <br>\- List <br> | * List <br>* List <br>* List <br>
1\. One <br>2\. Two <br>3\. Three | 1) One<br>2) Two<br>3) Three | 1. One<br>2. Two<br>3. Three
Horizontal Rule <br>\--- | Horizontal Rule<br>\*\*\* | Horizontal Rule <br><hr>
\`Inline code\` with backticks | |`Inline code` with backticks
\`\`\`<br>\# code block <br>print '3 backticks or'<br>print 'indent 4 spaces' <br>\`\`\` | ····\# code block<br>····print '3 backticks or'<br>····print 'indent 4 spaces' | \# code block <br>print '3 backticks or'<br>print 'indent 4 spaces'
::: spoiler hidden or nsfw stuff<br>*a bunch of spoilers here*<br>::: | | <details><summary> hidden or nsfw stuff </summary><p><em>a bunch of spoilers here</em></p></details>
Some ~subscript~ text | | Some <sub>subscript</sub> text
Some ^superscript^ text | | Some <sup>superscript</sup> text
[CommonMark Tutorial](https://commonmark.org/help/tutorial/)

31
docs/src/about_ranking.md

@ -0,0 +1,31 @@
# Trending / Hot / Best Sorting algorithm
## Goals
- During the day, new posts and comments should be near the top, so they can be voted on.
- After a day or so, the time factor should go away.
- Use a log scale, since votes tend to snowball, and so the first 10 votes are just as important as the next hundred.
## Reddit Sorting
[Reddit's comment sorting algorithm](https://medium.com/hacking-and-gonzo/how-reddit-ranking-algorithms-work-ef111e33d0d9), the wilson confidence sort, is inadequate, because it completely ignores time. What ends up happening, especially in smaller subreddits, is that the early comments end up getting upvoted, and newer comments stay at the bottom, never to be seen. Research showed that nearly all top comments are just the [first ones posted.](https://minimaxir.com/2016/11/first-comment/)
## Hacker News Sorting
The [Hacker New's ranking algorithm](https://medium.com/hacking-and-gonzo/how-hacker-news-ranking-algorithm-works-1d9b0cf2c08d) is great, but it doesn't use a log scale for the scores.
## My Algorithm
```
Rank = ScaleFactor * log(Max(1, 3 + Score)) / (Time + 2)^Gravity
Score = Upvotes - Downvotes
Time = time since submission (in hours)
Gravity = Decay gravity, 1.8 is default
```
- Lemmy uses the same `Rank` algorithm above, in two sorts: `Active`, and `Hot`.
- `Active` uses the post votes, and latest comment time (limited to two days).
- `Hot` uses the post votes, and the post published time.
- Use Max(1, score) to make sure all comments are affected by time decay.
- Add 3 to the score, so that everything that has less than 3 downvotes will seem new. Otherwise all new comments would stay at zero, near the bottom.
- The sign and abs of the score are necessary for dealing with the log of negative scores.
- A scale factor of 10k gets the rank in integer form.
A plot of rank over 24 hours, of scores of 1, 5, 10, 100, 1000, with a scale factor of 10k.
![](https://raw.githubusercontent.com/LemmyNet/lemmy/main/docs/img/rank_algorithm.png)

3
docs/src/administration.md

@ -0,0 +1,3 @@
# Admin info
Information for Lemmy instance admins, and those who want to start an instance.

83
docs/src/administration_backup_and_restore.md

@ -0,0 +1,83 @@
# Backup and Restore Guide
## Docker and Ansible
When using docker or ansible, there should be a `volumes` folder, which contains both the database, and all the pictures. Copy this folder to the new instance to restore your data.
### Incremental Database backup
To incrementally backup the DB to an `.sql` file, you can run:
```bash
docker-compose exec postgres pg_dumpall -c -U lemmy > lemmy_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql
```
### A Sample backup script
```bash
#!/bin/sh
# DB Backup
ssh [email protected]_IP "docker-compose exec postgres pg_dumpall -c -U lemmy" > ~/BACKUP_LOCATION/INSTANCE_NAME_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql
# Volumes folder Backup
rsync -avP -zz --rsync-path="sudo rsync" [email protected]_IP:/LEMMY_LOCATION/volumes ~/BACKUP_LOCATION/FOLDERNAME
```
### Restoring the DB
If you need to restore from a `pg_dumpall` file, you need to first clear out your existing database
```bash
# Drop the existing DB
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
# Restore from the .sql backup
cat db_dump.sql | docker exec -i FOLDERNAME_postgres_1 psql -U lemmy # restores the db
# This also might be necessary when doing a db import with a different password.
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "alter user lemmy with password 'bleh'"
```
### Changing your domain name
If you haven't federated yet, you can change your domain name in the DB. **Warning: do not do this after you've federated, or it will break federation.**
Get into `psql` for your docker:
`docker-compose exec postgres psql -U lemmy`
```
-- Post
update post set ap_id = replace (ap_id, 'old_domain', 'new_domain');
update post set url = replace (url, 'old_domain', 'new_domain');
update post set body = replace (body, 'old_domain', 'new_domain');
update post set thumbnail_url = replace (thumbnail_url, 'old_domain', 'new_domain');
delete from post_aggregates_fast;
insert into post_aggregates_fast select * from post_aggregates_view;
-- Comments
update comment set ap_id = replace (ap_id, 'old_domain', 'new_domain');
update comment set content = replace (content, 'old_domain', 'new_domain');
delete from comment_aggregates_fast;
insert into comment_aggregates_fast select * from comment_aggregates_view;
-- User
update user_ set actor_id = replace (actor_id, 'old_domain', 'new_domain');
update user_ set avatar = replace (avatar, 'old_domain', 'new_domain');
delete from user_fast;
insert into user_fast select * from user_view;
-- Community
update community set actor_id = replace (actor_id, 'old_domain', 'new_domain');
delete from community_aggregates_fast;
insert into community_aggregates_fast select * from community_aggregates_view;
```
## More resources
- https://stackoverflow.com/questions/24718706/backup-restore-a-dockerized-postgresql-database

24
docs/src/administration_configuration.md

@ -0,0 +1,24 @@
# Configuration
The configuration is based on the file
[defaults.hjson](https://yerbamate.dev/LemmyNet/lemmy/src/branch/main/server/config/defaults.hjson).
This file also contains documentation for all the available options. To override the defaults, you
can copy the options you want to change into your local `config.hjson` file.
To use a different `config.hjson` location than the current directory, set the environment variable `LEMMY_CONFIG_LOCATION`. Make sure you copy the `defaults.hjson` if you do this, otherwise you will be missing settings.
Additionally, you can override any config files with environment variables. These have the same
name as the config options, and are prefixed with `LEMMY_`. For example, you can override the
`database.password` with `LEMMY_DATABASE__POOL_SIZE=10`.
An additional option `LEMMY_DATABASE_URL` is available, which can be used with a PostgreSQL
connection string like `postgres://lemmy:[email protected]_db:5432/lemmy`, passing all connection
details at once.
If the Docker container is not used, manually create the database specified above by running the
following commands:
```bash
cd server
./db-init.sh
```

25
docs/src/administration_install_ansible.md

@ -0,0 +1,25 @@
# Ansible Installation
This is the same as the [Docker installation](administration_install_docker.md), except that Ansible handles all of it automatically. It also does some extra things like setting up TLS and email for your Lemmy instance.
First, you need to [install Ansible on your local computer](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) (e.g. using `sudo apt install ansible`) or the equivalent for you platform.
Then run the following commands on your local computer:
```bash
git clone https://github.com/LemmyNet/lemmy.git
cd lemmy/ansible/
cp inventory.example inventory
nano inventory # enter your server, domain, contact email
# If the command below fails, you may need to comment out this line
# In the ansible.cfg file:
# interpreter_python=/usr/bin/python3
ansible-playbook lemmy.yml --become
```
To update to a new version, just run the following in your local Lemmy repo:
```bash
git pull origin main
cd ansible
ansible-playbook lemmy.yml --become
```

41
docs/src/administration_install_docker.md

@ -0,0 +1,41 @@
# Docker Installation
Make sure you have both docker and docker-compose(>=`1.24.0`) installed. On Ubuntu, just run `apt install docker-compose docker.io`. Next,
```bash
# create a folder for the lemmy files. the location doesnt matter, you can put this anywhere you want
mkdir /lemmy
cd /lemmy
# download default config files
wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/prod/docker-compose.yml
wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/lemmy.hjson
wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/iframely.config.local.js
# Set correct permissions for pictrs folder
mkdir -p volumes/pictrs
sudo chown -R 991:991 volumes/pictrs
```
After this, have a look at the [config file](administration_configuration.md) named `lemmy.hjson`, and adjust it, in particular the hostname, and possibly the db password. Then run:
`docker-compose up -d`
To make Lemmy available outside the server, you need to setup a reverse proxy, like Nginx. [A sample nginx config](https://raw.githubusercontent.com/LemmyNet/lemmy/main/ansible/templates/nginx.conf), could be setup with:
```bash
wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/ansible/templates/nginx.conf
# Replace the {{ vars }}
sudo mv nginx.conf /etc/nginx/sites-enabled/lemmy.conf
```
You will also need to setup TLS, for example with [Let's Encrypt](https://letsencrypt.org/). After this you need to restart Nginx to reload the config.
## Updating
To update to the newest version, you can manually change the version in `docker-compose.yml`. Alternatively, fetch the latest version from our git repo:
```bash
wget https://raw.githubusercontent.com/LemmyNet/lemmy/main/docker/prod/docker-compose.yml
docker-compose up -d
```

24
docs/src/administration_install_kubernetes.md

@ -0,0 +1,24 @@
# Kubernetes Installation
You'll need to have an existing Kubernetes cluster and [storage class](https://kubernetes.io/docs/concepts/storage/storage-classes/).
Setting this up will vary depending on your provider.
To try it locally, you can use [MicroK8s](https://microk8s.io/) or [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/).
Once you have a working cluster, edit the environment variables and volume sizes in `docker/k8s/*.yml`.
You may also want to change the service types to use `LoadBalancer`s depending on where you're running your cluster (add `type: LoadBalancer` to `ports)`, or `NodePort`s.
By default they will use `ClusterIP`s, which will allow access only within the cluster. See the [docs](https://kubernetes.io/docs/concepts/services-networking/service/) for more on networking in Kubernetes.
**Important** Running a database in Kubernetes will work, but is generally not recommended.
If you're deploying on any of the common cloud providers, you should consider using their managed database service instead (RDS, Cloud SQL, Azure Databse, etc.).
Now you can deploy:
```bash
# Add `-n foo` if you want to deploy into a specific namespace `foo`;
# otherwise your resources will be created in the `default` namespace.
kubectl apply -f docker/k8s/db.yml
kubectl apply -f docker/k8s/pictshare.yml
kubectl apply -f docker/k8s/lemmy.yml
```
If you used a `LoadBalancer`, you should see it in your cloud provider's console.

39
docs/src/contributing.md

@ -0,0 +1,39 @@
# Contributing
Information about contributing to Lemmy, whether it is translating, testing, designing or programming.
## Issue tracking / Repositories
- [GitHub (for issues and pull requests)](https://github.com/LemmyNet/lemmy)
- [Gitea (only for pull requests)](https://yerbamate.dev/LemmyNet/lemmy)
- [GitLab (only code-mirror)](https://gitlab.com/dessalines/lemmy)
## Translating
Check out [Lemmy's Weblate](https://weblate.yerbamate.dev/projects/lemmy/) for translations.
## Architecture
### 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`.
### 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:
- `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`)
- `routes` - The server endpoints .
- `apub` - The activitypub conversions.
- `websocket` - Creates the websocket server.
## Linting / Formatting
- Every front and back end commit is automatically formatted then linted using `husky`, and `lint-staged`.
- Rust with `cargo fmt` and `cargo clippy`.
- Typescript with `prettier` and `eslint`.

378
docs/src/contributing_apub_api_outline.md

@ -0,0 +1,378 @@
# Activitypub API outline
- Start with the [reddit API](https://www.reddit.com/dev/api), and find [Activitypub vocab](https://www.w3.org/TR/activitystreams-vocabulary/) to match it.
<!-- toc -->
- [Actors](#actors)
* [User / Person](#user--person)
* [Community / Group](#community--group)
- [Objects](#objects)
* [Post / Page](#post--page)
* [Post Listings / Ordered CollectionPage](#post-listings--ordered-collectionpage)
* [Comment / Note](#comment--note)
* [Comment Listings / Ordered CollectionPage](#comment-listings--ordered-collectionpage)
* [Deleted thing / Tombstone](#deleted-thing--tombstone)
- [Actions](#actions)
* [Comments](#comments)
+ [Create](#create)
+ [Delete](#delete)
+ [Update](#update)
+ [Read](#read)
+ [Like](#like)
+ [Dislike](#dislike)
* [Posts](#posts)
+ [Create](#create-1)
+ [Delete](#delete-1)
+ [Update](#update-1)
+ [Read](#read-1)
* [Communities](#communities)
+ [Create](#create-2)
+ [Delete](#delete-2)
+ [Update](#update-2)
+ [Join](#join)
+ [Leave](#leave)
* [Moderator](#moderator)
+ [Ban user from community / Block](#ban-user-from-community--block)
+ [Delete Comment](#delete-comment)
+ [Invite a moderator](#invite-a-moderator)
+ [Accept Invitation](#accept-invitation)
+ [Reject Invitation](#reject-invitation)
<!-- tocstop -->
## Actors
### [User / Person](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Person",
"id": "https://instance_url/api/v1/user/sally_smith",
"inbox": "https://instance_url/api/v1/user/sally_smith/inbox",
"outbox": "https://instance_url/api/v1/user/sally_smith/outbox",
"liked": "https://instance_url/api/v1/user/sally_smith/liked",
// TODO disliked?
"following": "https://instance_url/api/v1/user/sally_smith/following",
"name": "sally_smith",
"preferredUsername": "Sally",
"icon"?: {
"type": "Image",
"name": "User icon",
"url": "https://instance_url/api/v1/user/sally_smith/icon.png",
"width": 32,
"height": 32
},
"published": "2014-12-31T23:00:00-08:00",
"summary"?: "This is sally's profile."
}
```
### [Community / Group](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-group)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Group",
"id": "https://instance_url/api/v1/community/today_i_learned",
"name": "today_i_learned"
"attributedTo": [ // The moderators
"http://joe.example.org",
],
"followers": "https://instance_url/api/v1/community/today_i_learned/followers",
"published": "2014-12-31T23:00:00-08:00",
"summary"?: "The group's tagline",
"attachment: [{}] // TBD, these would be where strong types for custom styles, and images would work.
}
```
## Objects
### [Post / Page](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-page)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Page",
"id": "https://instance_url/api/v1/post/1",
"name": "The title of a post, maybe a link to imgur",
"url": "https://news.blah.com"
"attributedTo": "http://joe.example.org", // The poster
"published": "2014-12-31T23:00:00-08:00",
}
```
### [Post Listings / Ordered CollectionPage](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollectionpage)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "OrderedCollectionPage",
"id": "https://instance_url/api/v1/posts?type={all, best, front}&sort={}&page=1,
"partOf": "http://example.org/foo",
"orderedItems": [Posts]
}
```
### [Comment / Note](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"id": "https://instance_url/api/v1/comment/1",
"mediaType": "text/markdown",
"content": "Looks like it is going to rain today. Bring an umbrella *if necessary*!"
"attributedTo": john_id,
"inReplyTo": "comment or post id",
"published": "2014-12-31T23:00:00-08:00",
"updated"?: "2014-12-12T12:12:12Z"
"replies" // TODO, not sure if these objects should embed all replies in them or not.
"to": [sally_id, group_id]
}
```
### [Comment Listings / Ordered CollectionPage](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollectionpage)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "OrderedCollectionPage",
"id": "https://instance_url/api/v1/comments?type={all,user,community,post,parent_comment}&id=1&page=1,
"partOf": "http://example.org/foo",
"orderedItems": [Comments]
}
```
### [Deleted thing / Tombstone](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone)
```
{
"type": "Tombstone",
"formerType": "Note / Post",
"id": note / post_id,
"deleted": "2016-03-17T00:00:00Z"
}
```
## Actions
- These are all posts to a user's outbox.
- The server then creates a post to the necessary inbox of the recipient, or the followers.
- Whenever a user accesses the site, they do a get from their inbox.
### Comments
#### [Create](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-create)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": id,
"object": comment_id, or post_id
}
```
#### [Delete](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Delete",
"actor": id,
"object": comment_id, or post_id
}
```
#### [Update](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-update)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": id,
"object": comment_id, or post_id
"content": "New comment",
"updated": "New Date"
}
```
#### [Read](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-read)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Read",
"actor": user_id
"object": comment_id
}
```
#### [Like](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-like)
- TODO: Should likes be notifications? IE, have a to?
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Like",
"actor": user_id
"object": comment_id
// TODO different types of reactions, or no?
}
```
#### [Dislike](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-dislike)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Dislike",
"actor": user_id
"object": comment_id
// TODO different types of reactions, or no?
}
```
### Posts
#### [Create](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-create)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": id,
"to": community_id/followers
"object": post_id
}
```
#### [Delete](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Delete",
"actor": id,
"object": comment_id, or post_id
}
```
#### [Update](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-update)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": id,
"object": comment_id, or post_id
TODO fields.
}
```
#### [Read](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-read)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Read",
"actor": user_id
"object": post_id
}
```
### Communities
#### [Create](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-create)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": id,
"object": community_id
}
```
#### [Delete](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Delete",
"actor": id,
"object": community_id
}
```
#### [Update](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-update)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": id,
"object": community_id
TODO fields.
}
```
#### [Follow / Subscribe](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-follow)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Follow",
"actor": id
"object": community_id
}
```
#### [Ignore/ Unsubscribe](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-ignore)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Follow",
"actor": id
"object": community_id
}
```
#### [Join / Become a Mod](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-join)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Join",
"actor": user_id,
"object": community_id
}
```
#### [Leave](https://www.w3.org/TR/activitystreams-vocabulary#dfn-leave)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Leave",
"actor": user_id,
"object": community_id
}
```
### Moderator
#### [Ban user from community / Block](https://www.w3.org/TR/activitystreams-vocabulary#dfn-block)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Remove",
"actor": mod_id,
"object": user_id,
"origin": group_id
}
```
#### [Delete Comment](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Delete",
"actor": id,
"object": community_id
}
```
#### [Invite a moderator](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-invite)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Invite",
"id": "https://instance_url/api/v1/invite/1",
"actor": sally_id,
"object": group_id,
"target": john_id
}
```
#### [Accept Invitation](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Accept",
"actor": john_id,
"object": invite_id
}
```
#### [Reject Invitation](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-reject)
```
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Reject",
"actor": john_id,
"object": invite_id
}
```

40
docs/src/contributing_docker_development.md

@ -0,0 +1,40 @@
# Docker Development
## Setup
> **NOTE**: Workflow is designed for Mac/Linux - it will work on Windows with WSL, but you'll need to do some extra steps.
* **Mac** - Install [Docker Desktop for Mac](https://hub.docker.com/editions/community/docker-ce-desktop-mac) - this creates the VM that will build images, as well as installs other tools needed such as docker-compose. Once installed, go to settings and bump up the amount of RAM from 2gb to at least 4gb, or the build will fail. (8GB recommended)
* **Linux** - You'll need to install Docker and docker-compose - there is a different installation for Docker per distro, but here is the [Ubuntu/Debian](https://docs.docker.com/engine/install/ubuntu/) instructions. Once done, go to the [docker-compose install directions](https://docs.docker.com/compose/install/) and pick the Linux instructions.
## Running
```bash
sudo apt install git docker-compose
git clone https://github.com/LemmyNet/lemmy
cd lemmy/docker/dev
sudo docker-compose up --no-deps --build
```
Upon running this, it will take a while to build, especially on slower systems. Later builds will be faster, based on how Docker creates images (using point in time snapshots).
Also, trying to upload images will be broken until you set the permissions on your /volumes/pictrs folder - you need to run `chown -R 991:991 /volumes/pictrs` from the root of the project, and will likely need to restart the docker-compose after that.
Once everything is up and running, you can go to http://localhost:8536 to visit your local instance.
## Additional Steps
To speed up the Docker compile, add the following to `/etc/docker/daemon.json` and restart Docker.
```
{
"features": {
"buildkit": true
}
}
```
*(You can edit the daemon.json file on Docker Desktop under Settings > Docker Engine)*
If the build is still too slow, you will have to use a
[local build](contributing_local_development.md) instead.

88
docs/src/contributing_federation_development.md

@ -0,0 +1,88 @@
# Federation Development
## Setup
If you don't have a local clone of the Lemmy repo yet, just run the following command:
```bash
git clone https://github.com/LemmyNet/lemmy
```
## Running locally
You need to have the following packages installed, the Docker service needs to be running.
- docker
- docker-compose
Then run the following
```bash
cd docker/federation
./start-local-instances.bash
```
The federation test sets up 5 instances:
Instance | Username | Location | Notes
--- | --- | --- | ---
lemmy-alpha | lemmy_alpha | [127.0.0.1:8540](http://127.0.0.1:8540) | federated with all other instances
lemmy-beta | lemmy_beta | [127.0.0.1:8550](http://127.0.0.1:8550) | federated with all other instances
lemmy-gamma | lemmy_gamma | [127.0.0.1:8560](http://127.0.0.1:8560) | federated with all other instances
lemmy-delta | lemmy_delta | [127.0.0.1:8570](http://127.0.0.1:8570) | only allows federation with lemmy-beta
lemmy-epsilon | lemmy_epsilon | [127.0.0.1:8580](http://127.0.0.1:8580) | uses blocklist, has lemmy-alpha blocked
You can log into each using the instance name, and `lemmy` as the password, IE (`lemmy_alpha`, `lemmy`).
To start federation between instances, visit one of them and search for a user, community or post, like this:
- `[email protected]:8540`
- `http://lemmy-beta:8550/post/3`
- `@[email protected]:8560`
Firefox containers are a good way to test them interacting.
## Integration tests
To run a suite of suite of federation integration tests:
```bash
cd docker/federation
./run-tests.bash
```
## Running on a server
Note that federation is currently in alpha. **Only use it for testing**, not on any production server, and be aware that turning on federation may break your instance.
Follow the normal installation instructions, either with [Ansible](administration_install_ansible.md) or
[manually](administration_install_docker.md). Then replace the line `image: dessalines/lemmy:v0.x.x` in
`/lemmy/docker-compose.yml` with `image: dessalines/lemmy:federation`. Also add the following in
`/lemmy/lemmy.hjson`:
```
federation: {
enabled: true
tls_enabled: true,
allowed_instances: example.com,
}
```
Afterwards, and whenever you want to update to the latest version, run these commands on the server:
```
cd /lemmy/
sudo docker-compose pull
sudo docker-compose up -d
```
## Security Model
- HTTP signature verify: This ensures that activity really comes from the activity that it claims
- check_is_apub_valid : Makes sure its in our allowed instances list
- Lower level checks: To make sure that the user that creates/updates/removes a post is actually on the same instance as that post
For the last point, note that we are *not* checking whether the actor that sends the create activity for a post is
actually identical to the post's creator, or that the user that removes a post is a mod/admin. These things are checked
by the API code, and its the responsibility of each instance to check user permissions. This does not leave any attack
vector, as a normal instance user cant do actions that violate the API rules. The only one who could do that is the
admin (and the software deployed by the admin). But the admin can do anything on the instance, including send activities
from other user accounts. So we wouldnt actually gain any security by checking mod permissions or similar.

151
docs/src/contributing_local_development.md

@ -0,0 +1,151 @@
### Install build requirements
#### Ubuntu
```
sudo apt install git cargo libssl-dev pkg-config libpq-dev yarn curl gnupg2 espeak
# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
```
#### macOS
Install Rust using [the recommended option on rust-lang.org](https://www.rust-lang.org/tools/install) (rustup).
Then, install [Homebrew](https://brew.sh/) if you don't already have it installed.
Finally, install Node and Yarn.
```
brew install node yarn
```
### Get the source code
```
git clone https://github.com/LemmyNet/lemmy.git
# or alternatively from gitea
# 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)
```
cd ui
yarn
yarn build
```
### Setup postgresql
#### Ubuntu
```
sudo apt install postgresql
sudo systemctl start postgresql
# Either execute server/db-init.sh, or manually initialize the postgres database:
sudo -u postgres psql -c "create user lemmy with password 'password' superuser;" -U postgres
sudo -u postgres psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:[email protected]:5432/lemmy
```
#### macOS
```
brew install postgresql
brew services start postgresql
/usr/local/opt/postgres/bin/createuser -s postgres
# Either execute server/db-init.sh, or manually initialize the postgres database:
psql -c "create user lemmy with password 'password' superuser;" -U postgres
psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:[email protected]:5432/lemmy
```
### Run a local development instance
```
# run each of these in a seperate terminal
cd server && cargo run
cd ui && yarn start
```
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.
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"]
```
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).
### macOS
#### Build Requirements
```
# install homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew install git cargo [email protected] pkg-config libpq yarn curl gnupg2
```
#### Get the 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)
```
cd ui
yarn
yarn build
```
#### Setup postgresql
```
brew instal postgresql
brew services start postgresql
/usr/local/opt/postgres/bin/createuser -s postgres
cd server
./db-init.sh
```
#### Run a local development instance
```
# run each of these in a seperate terminal
cd server && cargo run
ui & yarn start
```
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.
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"]
```

16
docs/src/contributing_tests.md

@ -0,0 +1,16 @@
### Tests
#### Rust
After installing [local development dependencies](contributing_local_development.md), run the
following commands in the `server` subfolder:
```bash
psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
./test.sh
```
### Federation
Install the [Docker development dependencies](contributing_docker_development.md), and execute
`docker/federation-test/run-tests.sh`

18
docs/src/contributing_theming.md

@ -0,0 +1,18 @@
# Theming Guide
Lemmy uses [Bootstrap v4](https://getbootstrap.com/), and very few custom css classes, so any bootstrap v4 compatible theme should work fine.
## Creating
- Use a tool like [bootstrap.build](https://bootstrap.build/) to create a bootstrap v4 theme. Export the `bootstrap.min.css` once you're done, and save the `_variables.scss` too.
## Testing
- To test out a theme, you can either use your browser's web tools, or a plugin like stylus to copy-paste a theme, when viewing Lemmy.
## 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. Test locally
1. Do a pull request with those changes.

1884
docs/src/contributing_websocket_http_api.md
File diff suppressed because it is too large
View File

79
docs/src/lemmy_council.md

@ -0,0 +1,79 @@
# Lemmy Council
- A group of lemmy developers and users that use a well-defined democratic process to steer the project in a positive direction, keep it aligned to community goals, and resolve conflicts.
- Council members are also added as administrators to any official Lemmy instances.
## 1. What gets voted on
This section describes all the aspects of Lemmy where the council has decision making power, namely:
- Coding direction
- Priorities / Emphasis
- Controversial features (For example, an unpopular feature should be removed)
- Moderation and conflict resolution on:
- [dev.lemmy.ml](https://dev.lemmy.ml/)
- [github.com/LemmyNet/lemmy](https://github.com/LemmyNet/lemmy)
- [yerbamate.dev/LemmyNet/lemmy](https://yerbamate.dev/LemmyNet/lemmy)
- [weblate.yerbamate.dev/projects/lemmy/](https://weblate.yerbamate.dev/projects/lemmy/)
- Technical administration of dev.lemmy.ml
- Official Lemmy accounts
- [Mastodon](https://mastodon.social/@LemmyDev)
- [Liberapay](https://liberapay.com/Lemmy/)
- [Patreon](https://www.patreon.com/dessalines)
- Council membership changes
- Changes to these rules
## 2. Feedback and Activity Reports
Every week, the council should make a thread on Lemmy that details its activity during the past week, be it development, moderation, or anything else mentioned in 1.
At the same time, users can give feedback and suggestions in this thread. This should be taken into account by the council. Council members can call for a vote on any controversial issues, if they can't be resolved by discussion.
## 2. Voting Process
Most of the time, we keep each other up to date through the Matrix chat, and take informal decisions on uncontroversial issues. For example, a user clearly violating the site rules could be banned by a single person, or ideally after discussing it with at least one other member.
If an issue can not be resolved in this way, then any council member can call for a vote, which works in the following way:
- Any council member can call for a vote, on any topic mentioned in 1.
- This should be used if there is any controversy in the community, or between council members.
- Before taking any decision, there needs to be a discussion where every council member can
explain their position.
- Discussion should be taken with the goal of reaching a compromise that is acceptable for
everyone.
- After the discussion, voting is done through Matrix emojis (👍: yes, 👎: no, X: abstain) and must
stay open for at least two days.
- All members of the Lemmy council have equal voting power.
- Decisions should be reached unanimously, or nearly so. If this is not possible, at least
2/3 of votes must be in favour for the motion to pass.
- Once a decision is reached in this way, every member needs to abide by it.
## 4. Joining
- We use the following process: anyone who is active around Lemmy can recommend any other active person to join the council. This has to be approved by a majority of the council.
- Active users are defined as those who contribute to Lemmy in some way for at least an hour per week on average, doing things like reporting bugs, discussing rules and features, translating, promoting, developing, or doing other things that aim to improve Lemmy as a whole.
-> people should have joined at least a month ago.
- The member list is public.
- Note: we would like to have a process where community members can elect candidates for the council, but this is not realistic because a single user could easily create multiple accounts and cheat the vote.
- Limit growth to one new member per month at most.
## 5. Removing members
- Inactive members should be removed from the council after a few months of inactivity, and after receiving a notification about this.
- Members that dont follow binding council decisions should be removed.
- Any member can be removed in a vote.
## 6. Goals
- We encourage the membership of groups such as LGBT, religious or ethnic minorities, abuse victims, etc etc, and strive to create a safe space for them to express their opinions. We also support measures to increase participation by the previously mentioned groups.
- The following are banned, and will always be harshly punished: fascism, abuse, racism, sexism, etc etc,
## 7. Communication
- A private Matrix chat for all council members.
- (Once private communities are done) A private community on dev.lemmy.ml for issues.
## 8. Member List / Contact Info
General Contact [@LemmyDev Mastodon](https://mastodon.social/@LemmyDev)
- [Dessalines](https://dev.lemmy.ml/u/dessalines)
- [Nutomic](https://dev.lemmy.ml/u/nutomic)
- [AgreeableLandscape](https://dev.lemmy.ml/u/AgreeableLandscape)
- [fruechtchen](https://dev.lemmy.ml/u/fruechtchen)
- [kixiQu](https://dev.lemmy.ml/u/kixiQu)

23
index.html

@ -6,25 +6,22 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Primary Meta Tags -->
<title>Hexbear</title>
<meta name="title" content="Hexbear">
<meta name="description"
content="Hexbear is a leftist social platform for discussion, posting memes, and sharing content. Join a community today.">
<meta name="title" content="{{title}}">
<meta name="description" content="{{description}}">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.hexbear.net/">
<meta property="og:title" content="Hexbear">
<meta property="og:description"
content="Hexbear is a leftist social platform for discussion, posting memes, and sharing content. Join a community today!">
<meta property="og:image" content="https://www.hexbear.net/static/assets/meta-preview.png">
<meta property="og:url" content="{{url}}">
<meta property="og:title" content="{{title}}">
<meta property="og:description" content="{{description}}">
<meta property="og:image" content="{{image}}">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://www.hexbear.net/">
<meta property="twitter:title" content="Hexbear">
<meta property="twitter:description"
content="Hexbear is a leftist social platform for discussion, posting memes, and sharing content. Join a community today!">
<meta property="twitter:image" content="https://www.hexbear.net/static/assets/meta-preview.png">
<meta property="twitter:url" content="{{url}}">
<meta property="twitter:title" content="{{title}}">
<meta property="twitter:description" content="{{description}}">
<meta property="twitter:image" content="{{image}}">
<!-- Icons -->
<link rel="shortcut icon" type="image/svg+xml" href="/favicon.ico" />

6
package.json

@ -1,8 +1,8 @@
{
"name": "lemmy",
"description": "The official Lemmy UI",
"name": "hexbear-frontend",
"description": "The frontend for Hexbear",
"version": "1.0.0",
"author": "Dessalines",
"author": "Hexbear contributors",
"license": "AGPL-3.0-or-later",
"scripts": {
"api-test": "jest src/api_tests/api.spec.ts",

2
server/.eslintignore

@ -0,0 +1,2 @@
node_modules/
../node_modules

22
server/.eslintrc.json

@ -0,0 +1,22 @@
{
"env": {
"es2021": true,
"node": true
},
"extends": [
"airbnb-base"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12
},
"plugins": [
"@typescript-eslint"
],
"rules": {
//Eslint was complaining about valid imports, we'll disable this.
"import/no-unresolved": "off",
"import/extensions": "off"
},
"root": true //Stop eslint from scanning into parent directories.
}

118
server/.gitignore

@ -0,0 +1,118 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

678
server/LICENSE

@ -0,0 +1,678 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C)
2007 Hexbear
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Preamble
The GNU Affero
General Public License is a free, copyleft license for software and other kinds
of works, specifically designed to ensure cooperation with the community in the
case of network server software.
The licenses for most software and other
practical works are designed to take away your freedom to share and change the
works. By contrast, our General Public Licenses are intended to guarantee your
freedom to share and change all versions of a program--to make sure it remains
free software for all its users.
When we speak of free software, we are
referring to freedom, not price. Our General Public Licenses are designed to make
sure that you have the freedom to distribute copies of free software (and charge
for them if you wish), that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free programs, and
that you know you can do these things.
Developers that use our General Public
Licenses protect your rights with two steps: (1) assert copyright on the
software, and (2) offer you this License which gives you legal permission to
copy, distribute and/or modify the software.
A secondary benefit of defending
all users' freedom is that improvements made in alternate versions of the
program, if they receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and encouraged by the
resulting cooperation. However, in the case of software used on network servers,
this result may fail to come about. The GNU General Public License permits making
a modified version and letting the public access it on a server without ever
releasing its source code to the public.
The GNU Affero General Public License
is designed specifically to ensure that, in such cases, the modified source code
becomes available to the community. It requires the operator of a network server
to provide the source code of the modified version running there to the users of
that server. Therefore, public use of a modified version, on a publicly
accessible server, gives the public access to the source code of the modified
version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is a
different license, not a version of the Affero GPL, but Affero has released a new
version of the Affero GPL which permits relicensing under this license.
The
precise terms and conditions for copying, distribution and modification
follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to
version 3 of the GNU Affero General Public License.
"Copyright" also means
copyright-like laws that apply to other kinds of works, such as semiconductor
masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and "recipients" may be
individuals or organizations.
To "modify" a work means to copy from or adapt
all or part of the work in a fashion requiring copyright permission, other than
the making of an exact copy. The resulting work is called a "modified version" of
the earlier work or a work "based on" the earlier work.
A "covered work"
means either the unmodified Program or a work based on the Program.
To
"propagate" a work means to do anything with it that, without permission, would
make you directly or secondarily liable for infringement under applicable
copyright law, except executing it on a computer or modifying a private copy.
Propagation includes copying, distribution (with or without modification), making
available to the public, and in some countries other activities as well.
To
"convey" a work means any kind of propagation that enables other parties to make
or receive copies. Mere interaction with a user through a computer network, with
no transfer of a copy, is not conveying.
An interactive user interface
displays "Appropriate Legal Notices" to the extent that it includes a convenient
and prominently visible feature that (1) displays an appropriate copyright
notice, and (2) tells the user that there is no warranty for the work (except to
the extent that warranties are provided), that licensees may convey the work
under this License, and how to view a copy of this License. If the interface
presents a list of user commands or options, such as a menu, a prominent item in
the list meets this criterion.
1. Source Code.
The "source code" for a
work means the preferred form of the work for making modifications to it. "Object
code" means any non-source form of a work.
A "Standard Interface" means an
interface that either is an official standard defined by a recognized standards
body, or, in the case of interfaces specified for a particular programming
language, one that is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other than the
work as a whole, that (a) is included in the normal form of packaging a Major
Component, but which is not part of that Major Component, and (b) serves only to
enable use of the work with that Major Component, or to implement a Standard
Interface for which an implementation is available to the public in source code
form. A "Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system (if any) on
which the executable work runs, or a compiler used to produce the work, or an
object code interpreter used to run it.
The "Corresponding Source" for a work
in object code form means all the source code needed to generate, install, and
(for an executable work) run the object code and to modify the work, including
scripts to control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free programs
which are used unmodified in performing those activities but which are not part
of the work. For example, Corresponding Source includes interface definition
files associated with source files for the work, and the source code for shared
libraries and dynamically linked subprograms that the work is specifically
designed to require, such as by intimate data communication or control flow
between those
subprograms and other parts of the work.
The Corresponding
Source need not include anything that users can regenerate automatically from
other parts of the Corresponding Source.
The Corresponding Source for a work
in source code form is that same work.
2. Basic Permissions.
All rights
granted under this License are granted for the term of copyright on the Program,
and are irrevocable provided the stated conditions are met. This License
explicitly affirms your unlimited permission to run the unmodified Program. The
output from running a covered work is covered by this License only if the output,
given its content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may
make, run and propagate covered works that you do not convey, without conditions
so long as your license otherwise remains in force. You may convey covered works
to others for the sole purpose of having them make modifications exclusively for
you, or provide you with facilities for running those works, provided that you
comply with the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works for you
must do so exclusively on your behalf, under your direction and control, on terms
that prohibit them from making any copies of your copyrighted material outside
their relationship with you.
Conveying under any other circumstances is
permitted solely under the conditions stated below. Sublicensing is not allowed;
section 10 makes it unnecessary.
3. Protecting Users' Legal Rights From
Anti-Circumvention Law.
No covered work shall be deemed part of an effective
technological measure under any applicable law fulfilling obligations under
article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar
laws prohibiting or restricting circumvention of such measures.
When you
convey a covered work, you waive any legal power to forbid circumvention of
technological measures to the extent such circumvention is effected by exercising
rights under this License with respect to the covered work, and you disclaim any
intention to limit operation or modification of the work as a means of enforcing,
against the work's users, your or third parties' legal rights to forbid
circumvention of technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you receive it, in
any medium, provided that you conspicuously and appropriately publish on each
copy an appropriate copyright notice; keep intact all notices stating that this
License and any non-permissive terms added in accord with section 7 apply to the
code; keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any
price or no price for each copy that you convey, and you may offer support or
warranty protection for a fee.
5. Conveying Modified Source Versions.
You
may convey a work based on the Program, or the modifications to produce it from
the Program, in the form of source code under the terms of section 4, provided
that you also meet all of these conditions:
a) The work must carry
prominent notices stating that you modified it, and giving a relevant date.
b) The work must carry prominent notices stating that it is released under this
License and any conditions added under section 7. This requirement modifies the
requirement in section 4 to "keep intact all notices".
c) You must license
the entire work, as a whole, under this License to anyone who comes into
possession of a copy. This License will therefore apply, along with any
applicable section 7 additional terms, to the whole of the work, and all its
parts, regardless of how they are packaged. This License gives no permission to
license the work in any other way, but it does not invalidate such permission if
you have separately received it.
d) If the work has interactive user
interfaces, each must display Appropriate Legal Notices; however, if the Program
has interactive interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other
separate and independent works, which are not by their nature extensions of the
covered work, and which are not combined with it such as to form a larger
program, in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not used to limit
the access or legal rights of the compilation's users beyond what the individual
works permit. Inclusion of a covered work in an aggregate does not cause this
License to apply to the other parts of the aggregate.
6. Conveying Non-Source
Forms.
You may convey a covered work in object code form under the terms of
sections 4 and 5, provided that you also convey the machine-readable
Corresponding Source under the terms of this License, in one of these ways:
a) Convey the object code in, or embodied in, a physical product (including a
physical distribution medium), accompanied by the Corresponding Source fixed on a
durable physical medium customarily used for software interchange.
b)
Convey the object code in, or embodied in, a physical product (including a
physical distribution medium), accompanied by a written offer, valid for at least
three years and valid for as long as you offer spare parts or customer support
for that product model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the product that is
covered by this License, on a durable physical medium customarily used for
software interchange, for a price no more than your reasonable cost of physically
performing this conveying of source, or (2) access to copy the Corresponding
Source from a network server at no charge.
c) Convey individual copies of
the object code with a copy of the written offer to provide the Corresponding
Source. This alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord with
subsection 6b.
d) Convey the object code by offering access from a
designated place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no further charge.
You need not require recipients to copy the Corresponding Source along with the
object code. If the place to copy the object code is a network server, the
Corresponding Source may be on a different server (operated by you or a third
party) that supports equivalent copying facilities, provided you maintain clear
directions next to the object code saying where to find the Corresponding Source.
Regardless of what server hosts the Corresponding Source, you remain obligated to
ensure that it is available for as long as needed to satisfy these
requirements.
e) Convey the object code using peer-to-peer transmission,
provided you inform other peers where the object code and Corresponding Source of
the work are being offered to the general public at no charge under subsection
6d.