diff --git a/.travis.yml b/.travis.yml index d765ecb4f..602a8613d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ before_cache: - rm -rfv target/debug/build/lemmy_server-* - rm -rfv target/debug/deps/lemmy_server-* - rm -rfv target/debug/lemmy_server.d - - cargo clean before_script: - psql -c "create user lemmy with password 'password' superuser;" -U postgres - psql -c 'create database lemmy with owner lemmy;' -U postgres diff --git a/ansible/VERSION b/ansible/VERSION index f9fc17d61..83ca525b7 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.6.39 +v0.6.44 diff --git a/ansible/lemmy.yml b/ansible/lemmy.yml index 8d5e22641..bc01623fc 100644 --- a/ansible/lemmy.yml +++ b/ansible/lemmy.yml @@ -36,13 +36,17 @@ - { src: 'templates/docker-compose.yml', dest: '/lemmy/docker-compose.yml', mode: '0600' } - { src: 'templates/nginx.conf', dest: '/etc/nginx/sites-enabled/lemmy.conf', mode: '0644' } - { src: '../docker/iframely.config.local.js', dest: '/lemmy/iframely.config.local.js', mode: '0600' } + vars: + lemmy_docker_image: "dessalines/lemmy:{{ lookup('file', 'VERSION') }}" + lemmy_port: "8536" + pictshare_port: "8537" + iframely_port: "8538" - name: add config file (only during initial setup) template: src='templates/config.hjson' dest='/lemmy/lemmy.hjson' mode='0600' force='no' owner='1000' group='1000' vars: postgres_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/postgres chars=ascii_letters,digits') }}" jwt_password: "{{ lookup('password', 'passwords/{{ inventory_hostname }}/jwt chars=ascii_letters,digits') }}" - lemmy_docker_image: "dessalines/lemmy:{{ lookup('file', 'VERSION') }}" - name: enable and start docker service systemd: diff --git a/ansible/templates/docker-compose.yml b/ansible/templates/docker-compose.yml index ffd5b9c2b..a4d54f6d8 100644 --- a/ansible/templates/docker-compose.yml +++ b/ansible/templates/docker-compose.yml @@ -7,7 +7,7 @@ services: - "127.0.0.1:8536:8536" restart: always environment: - - RUST_LOG=debug + - RUST_LOG=error volumes: - ./lemmy.hjson:/config/config.hjson:ro depends_on: diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index aa0c2a8e7..d9ffc2f38 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -10,7 +10,7 @@ RUN yarn install --pure-lockfile COPY ui /app/ui RUN yarn build -FROM ekidd/rust-musl-builder:1.40.0-openssl11 as rust +FROM ekidd/rust-musl-builder:1.42.0-openssl11 as rust # Cache deps WORKDIR /app @@ -33,7 +33,7 @@ RUN cargo build --frozen --release # RUN cargo install diesel_cli --no-default-features --features postgres -FROM ekidd/rust-musl-builder:1.40.0-openssl11 as docs +FROM ekidd/rust-musl-builder:1.42.0-openssl11 as docs WORKDIR /app COPY docs ./docs RUN sudo chown -R rust:rust . diff --git a/docker/dev/Dockerfile.libc b/docker/dev/Dockerfile.libc index 5eec38958..6348342fd 100644 --- a/docker/dev/Dockerfile.libc +++ b/docker/dev/Dockerfile.libc @@ -20,7 +20,7 @@ COPY ui /app/ui RUN yarn build -FROM rust:1.40 as rust +FROM rust:1.42 as rust # Cache deps WORKDIR /app @@ -53,7 +53,7 @@ RUN cp /app/server/target/release/lemmy_server /app/server/ready #RUN cp /app/server/target/debug/lemmy_server /app/server/ready -FROM rust:1.40 as docs +FROM rust:1.42 as docs WORKDIR /app diff --git a/docker/lemmy.hjson b/docker/lemmy.hjson index 5a6d1ff11..b61ea8261 100644 --- a/docker/lemmy.hjson +++ b/docker/lemmy.hjson @@ -41,7 +41,16 @@ # interval length for registration limit register_per_second: 3600 } -# # email sending configuration +# # optional: parameters for automatic configuration of new instance (only used at first start) +# setup: { +# # username for the admin user +# admin_username: "lemmy" +# # password for the admin user +# admin_password: "lemmy" +# # name of the site (can be changed later) +# site_name: "Lemmy Test" +# } +# # optional: email sending configuration # email: { # # hostname of the smtp server # smtp_server: "" diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 9f5e89259..76f5ae14d 100644 --- a/docker/prod/docker-compose.yml +++ b/docker/prod/docker-compose.yml @@ -12,12 +12,12 @@ services: restart: always lemmy: - image: dessalines/lemmy:v0.6.39 + image: dessalines/lemmy:v0.6.44 ports: - "127.0.0.1:8536:8536" restart: always environment: - - RUST_LOG=debug + - RUST_LOG=error volumes: - ./lemmy.hjson:/config/config.hjson:ro depends_on: diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 10a6153ea..70c423c79 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -13,6 +13,7 @@ - [Contributing](contributing.md) - [Docker Development](contributing_docker_development.md) - [Local Development](contributing_local_development.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) diff --git a/docs/src/about_goals.md b/docs/src/about_goals.md index 1463eeb07..caa6948a0 100644 --- a/docs/src/about_goals.md +++ b/docs/src/about_goals.md @@ -47,3 +47,7 @@ - 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) diff --git a/docs/src/administration_install_ansible.md b/docs/src/administration_install_ansible.md index 875dae6a1..bf5e67493 100644 --- a/docs/src/administration_install_ansible.md +++ b/docs/src/administration_install_ansible.md @@ -1,5 +1,7 @@ # 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: @@ -11,3 +13,10 @@ cp inventory.example inventory nano inventory # enter your server, domain, contact email ansible-playbook lemmy.yml --become ``` + +To update to a new version, just run the following in your local Lemmy repo: +```bash +git pull origin master +cd ansible +ansible-playbook lemmy.yml --become +``` \ No newline at end of file diff --git a/docs/src/administration_install_docker.md b/docs/src/administration_install_docker.md index 992049839..391299b3d 100644 --- a/docs/src/administration_install_docker.md +++ b/docs/src/administration_install_docker.md @@ -1,29 +1,33 @@ # Docker Installation -Make sure you have both docker and docker-compose(>=`1.24.0`) installed: +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 -mkdir lemmy/ -cd lemmy/ +# 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/dessalines/lemmy/master/docker/prod/docker-compose.yml wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/lemmy.hjson wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/iframely.config.local.js -# Edit lemmy.hjson, and docker-compose.yml to do more configuration (like adding a custom password) docker-compose up -d ``` -and go to http://localhost:8536. +After this, have a look at the [config file](administration_configuration.md) named `lemmy.hjson`, and adjust it, in particular the hostname. -[A sample nginx config](/ansible/templates/nginx.conf) (Note: Avatar / Image uploading won't work without this), could be setup with: +To make Lemmy available outside the server, you need to setup a reverse proxy, like Nginx. [A sample nginx config](/ansible/templates/nginx.conf), could be setup with: ```bash wget https://raw.githubusercontent.com/dessalines/lemmy/master/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, run: +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/dessalines/lemmy/master/docker/prod/docker-compose.yml diff --git a/docs/src/contributing.md b/docs/src/contributing.md index e73cc4b9b..9a01ad5d9 100644 --- a/docs/src/contributing.md +++ b/docs/src/contributing.md @@ -2,6 +2,12 @@ Information about contributing to Lemmy, whether it is translating, testing, designing or programming. +## Issue tracking / Repositories + +- [GitHub (for issues)](https://github.com/dessalines/lemmy) +- [Gitea](https://yerbamate.dev/dessalines/lemmy) +- [GitLab](https://gitlab.com/dessalines/lemmy) + ## Translating Go [here](https://github.com/dessalines/lemmy#translations) for translation instructions. diff --git a/docs/src/contributing_federation_development.md b/docs/src/contributing_federation_development.md new file mode 100644 index 000000000..13a047d08 --- /dev/null +++ b/docs/src/contributing_federation_development.md @@ -0,0 +1,37 @@ +# 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://yerbamate.dev/nutomic/lemmy.git -b federation +``` + +If you already have the Lemmy repo cloned, you need to add a new remote: +```bash +git remote add federation https://yerbamate.dev/nutomic/lemmy.git +git checkout federation +git pull federation federation +``` + +## Running + +You need to have the following packages installed, the Docker service needs to be running. + +- docker +- docker-compose +- cargo +- yarn + +Then run the following +```bash +cd dev/federation-test +./run-federation-test.bash +``` + +After the build is finished and the docker-compose setup is running, open [127.0.0.1:8540](http://127.0.0.1:8540) and +[127.0.0.1:8541](http://127.0.0.1:8541) in your browser to use the test instances. You can login as admin with +username `lemmy` and password `lemmy`, or create new accounts. + +Please get in touch if you want to contribute to this, so we can coordinate things and avoid duplicate work. \ No newline at end of file diff --git a/server/Cargo.lock b/server/Cargo.lock index ee1b922bc..9cef0b2c4 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [[package]] name = "activitystreams" -version = "0.5.0-alpha.15" +version = "0.5.0-alpha.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "activitystreams-derive 0.5.0-alpha.7 (registry+https://github.com/rust-lang/crates.io-index)", + "activitystreams-derive 0.5.0-alpha.8 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", @@ -16,7 +16,7 @@ dependencies = [ [[package]] name = "activitystreams-derive" -version = "0.5.0-alpha.7" +version = "0.5.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -355,6 +355,14 @@ dependencies = [ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "arc-swap" version = "0.4.5" @@ -564,15 +572,6 @@ name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "bytes" version = "0.5.4" @@ -608,22 +607,17 @@ dependencies = [ ] [[package]] -name = "chttp" -version = "0.5.5" +name = "clap" +version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "curl 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", - "curl-sys 0.4.30+curl-7.69.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sluice 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -634,6 +628,22 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "comrak" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "entities 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "twoway 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "config" version = "0.10.1" @@ -676,14 +686,6 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-channel" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-channel" version = "0.4.2" @@ -693,15 +695,6 @@ dependencies = [ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-utils" version = "0.7.2" @@ -956,6 +949,11 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "enum-as-inner" version = "0.3.2" @@ -1083,24 +1081,11 @@ dependencies = [ "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "futures-channel-preview" -version = "0.3.0-alpha.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "futures-core" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "futures-core-preview" -version = "0.3.0-alpha.17" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "futures-executor" version = "0.3.4" @@ -1116,11 +1101,6 @@ name = "futures-io" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "futures-io-preview" -version = "0.3.0-alpha.17" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "futures-macro" version = "0.3.4" @@ -1160,18 +1140,6 @@ dependencies = [ "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "futures-util-preview" -version = "0.3.0-alpha.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "fxhash" version = "0.2.1" @@ -1267,16 +1235,6 @@ name = "htmlescape" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "http" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "http" version = "0.2.0" @@ -1342,6 +1300,28 @@ dependencies = [ "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "isahc" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "curl 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", + "curl-sys 0.4.30+curl-7.69.1 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sluice 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.1.1" @@ -1401,7 +1381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "lemmy_server" version = "0.0.1" dependencies = [ - "activitystreams 0.5.0-alpha.15 (registry+https://github.com/rust-lang/crates.io-index)", + "activitystreams 0.5.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "actix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "actix-files 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-rt 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1409,7 +1389,7 @@ dependencies = [ "actix-web-actors 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "bcrypt 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "chttp 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "comrak 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1418,11 +1398,13 @@ dependencies = [ "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "isahc 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "jsonwebtoken 7.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "lettre_email 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1430,8 +1412,8 @@ dependencies = [ "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1540,6 +1522,11 @@ dependencies = [ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "match_cfg" version = "0.1.0" @@ -1815,6 +1802,45 @@ name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pin-project" version = "0.4.8" @@ -2323,6 +2349,17 @@ dependencies = [ "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sha1" version = "0.6.0" @@ -2365,12 +2402,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "sluice" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2404,6 +2442,11 @@ name = "strsim" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.9.3" @@ -2411,12 +2454,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "strum" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "strum_macros" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2467,6 +2510,14 @@ dependencies = [ "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thiserror" version = "1.0.11" @@ -2606,11 +2657,35 @@ dependencies = [ "trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "twoway" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unchecked-index 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typed-arena" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typenum" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unchecked-index" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicase" version = "2.6.0" @@ -2640,11 +2715,21 @@ name = "unicode-segmentation" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "untrusted" version = "0.7.0" @@ -2706,6 +2791,11 @@ name = "vcpkg" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.5" @@ -2855,8 +2945,8 @@ dependencies = [ ] [metadata] -"checksum activitystreams 0.5.0-alpha.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1ecff5b18578fac8cff5f139009cd0a4f605b6f5eddc91f2b8319bfef76cc2c3" -"checksum activitystreams-derive 0.5.0-alpha.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d7498811842309cc5b54c123bcf328e33eebe9841037067b1e9b93caa820085d" +"checksum activitystreams 0.5.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "e7173513c9d586a1157f375835777e3b50498b6b7aab4411a7098b455ba995f0" +"checksum activitystreams-derive 0.5.0-alpha.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c7ff4a2be3b67d763e78794f622ef2d53da077521229774837f61963c4067b36" "checksum actix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4af87564ff659dee8f9981540cac9418c45e910c8072fdedd643a262a38fcaf" "checksum actix-codec 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09e55f0a5c2ca15795035d90c46bd0e73a5123b72f68f12596d6ba5282051380" "checksum actix-connect 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c95cc9569221e9802bf4c377f6c18b90ef10227d787611decf79fd47d2a8e76c" @@ -2878,6 +2968,7 @@ dependencies = [ "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arc-swap 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" @@ -2904,22 +2995,20 @@ dependencies = [ "checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" "checksum bytestring 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fc267467f58ef6cc8874064c62a0423eb0d099362c8a23edd1c6d044f46eead4" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" -"checksum chttp 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ca699a61cc91c90af209d6bf546fc69d29e9d474c77fa86b9410eb3cf1e6eb31" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum comrak 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e17058cc536cf290563e88787d7b9e6030ce4742943017cc2ffb71f88034021c" "checksum config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" "checksum copyless 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff9c56c9fb2a49c05ef0e431485a22400af20d33226dc0764d891d09e724127" "checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" "checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" "checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" "checksum curl 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)" = "eda1c0c03cacf3365d84818a40293f0e3f3953db8759c9c565a3b434edf0b52e" "checksum curl-sys 0.4.30+curl-7.69.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923b38e423a8f47a4058e96f2a1fa2865a6231097ee860debd678d244277d50c" @@ -2947,6 +3036,7 @@ dependencies = [ "checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" "checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" "checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" +"checksum entities 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" "checksum enum-as-inner 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4bfcfacb61d231109d1d55202c1f33263319668b168843e02ad4652725ec9c" "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" "checksum failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" @@ -2962,17 +3052,13 @@ dependencies = [ "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" "checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" -"checksum futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "21c71ed547606de08e9ae744bb3c6d80f5627527ef31ecf2a7210d0e67bc8fae" "checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" -"checksum futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4b141ccf9b7601ef987f36f1c0d9522f76df3bba1cf2e63bfacccc044c4558f5" "checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" "checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" -"checksum futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "082e402605fcb8b1ae1e5ba7d7fdfd3e31ef510e2a8367dd92927bb41ae41b3a" "checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" "checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" "checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" "checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" -"checksum futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "af8198c48b222f02326940ce2b3aa9e6e91a32886eeaad7ca3b8e4c70daa3f4e" "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" @@ -2983,7 +3069,6 @@ dependencies = [ "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" "checksum hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" "checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" -"checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" "checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" @@ -2992,6 +3077,7 @@ dependencies = [ "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum ipconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" +"checksum isahc 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50bdb3bdcbf6d534daaad1a686eda0d0dc1946818fa71e3edd3124d001adfdc2" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" "checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" "checksum js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" @@ -3011,6 +3097,7 @@ dependencies = [ "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" @@ -3042,6 +3129,10 @@ dependencies = [ "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1581760c757a756a41f0ee3ff01256227bdf64cb752839779b95ffb01c59793" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +"checksum pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +"checksum pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" "checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" "checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" "checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" @@ -3103,24 +3194,27 @@ dependencies = [ "checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" "checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" "checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" "checksum simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum sluice 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec70d7c3b17c262d4a18f7291c6ce62bf47170915f3b795434d3c5c49a4e59b7" +"checksum sluice 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fed13b7cb46f13a15db2c4740f087a848acc8b31af89f95844d40137451f89b1" "checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" -"checksum strum 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "530efb820d53b712f4e347916c5e7ed20deb76a4f0457943b3182fb889b06d2c" -"checksum strum_macros 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6e163a520367c465f59e0a61a23cfae3b10b6546d78b6f672a382be79f7110" +"checksum strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" +"checksum strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" "checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" "checksum thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" @@ -3133,12 +3227,18 @@ dependencies = [ "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum trust-dns-proto 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f" "checksum trust-dns-resolver 0.18.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f" +"checksum twoway 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6b40075910de3a912adbd80b5d8bad6ad10a23eeb1f5bf9d4006839e899ba5bc" +"checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +"checksum unchecked-index 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" "checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" "checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" @@ -3147,6 +3247,7 @@ dependencies = [ "checksum v_escape_derive 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c2ca2a14bc3fc5b64d188b087a7d3a927df87b152e941ccfbc66672e20c467ae" "checksum v_htmlescape 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e33e939c0d8cf047514fb6ba7d5aac78bc56677a6938b2ee67000b91f2e97e41" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" diff --git a/server/Cargo.toml b/server/Cargo.toml index 548406fa8..d94d56401 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -8,12 +8,12 @@ edition = "2018" diesel = { version = "1.4.2", features = ["postgres","chrono", "r2d2", "64-column-tables"] } diesel_migrations = "1.4.0" dotenv = "0.15.0" -bcrypt = "0.6.1" -activitystreams = "0.5.0-alpha.15" +activitystreams = "0.5.0-alpha.16" +bcrypt = "0.6.2" chrono = { version = "0.4.7", features = ["serde"] } failure = "0.1.5" -serde_json = { version = "1.0.45", features = ["preserve_order"]} -serde = { version = "1.0.94", features = ["derive"] } +serde_json = { version = "1.0.48", features = ["preserve_order"]} +serde = { version = "1.0.105", features = ["derive"] } actix = "0.9.0" actix-web = "2.0.0" actix-files = "0.2.1" @@ -22,10 +22,10 @@ actix-rt = "1.0.0" log = "0.4.0" env_logger = "0.7.1" rand = "0.7.3" -strum = "0.17.1" -strum_macros = "0.17.1" +strum = "0.18.0" +strum_macros = "0.18.0" jsonwebtoken = "7.0.1" -regex = "1.3.4" +regex = "1.3.5" lazy_static = "1.3.0" lettre = "0.9.2" lettre_email = "0.9.2" @@ -36,4 +36,6 @@ config = "0.10.1" hjson = "0.8.2" url = "2.1.1" percent-encoding = "2.1.0" -chttp = "0.5.5" +isahc = "0.9" +comrak = "0.7" +openssl = "0.10" diff --git a/server/config/defaults.hjson b/server/config/defaults.hjson index e048ced89..8603e49a8 100644 --- a/server/config/defaults.hjson +++ b/server/config/defaults.hjson @@ -1,4 +1,15 @@ { +# # optional: parameters for automatic configuration of new instance (only used at first start) +# setup: { +# # username for the admin user +# admin_username: "" +# # password for the admin user +# admin_password: "" +# # optional: email for the admin user (can be omitted and set later through the website) +# admin_email: "" +# # name of the site (can be changed later) +# site_name: "" +# } # settings related to the postgresql database database: { # username to connect to postgres diff --git a/server/migrations/2020-03-26-192410_add_activitypub_tables/down.sql b/server/migrations/2020-03-26-192410_add_activitypub_tables/down.sql new file mode 100644 index 000000000..b1710623e --- /dev/null +++ b/server/migrations/2020-03-26-192410_add_activitypub_tables/down.sql @@ -0,0 +1,16 @@ +drop table activity; + +alter table user_ +drop column actor_id, +drop column private_key, +drop column public_key, +drop column bio, +drop column local, +drop column last_refreshed_at; + +alter table community +drop column actor_id, +drop column private_key, +drop column public_key, +drop column local, +drop column last_refreshed_at; diff --git a/server/migrations/2020-03-26-192410_add_activitypub_tables/up.sql b/server/migrations/2020-03-26-192410_add_activitypub_tables/up.sql new file mode 100644 index 000000000..8fe3b8ed1 --- /dev/null +++ b/server/migrations/2020-03-26-192410_add_activitypub_tables/up.sql @@ -0,0 +1,36 @@ +-- The Activitypub activity table +-- All user actions must create a row here. +create table activity ( + id serial primary key, + user_id int references user_ on update cascade on delete cascade not null, -- Ensures that the user is set up here. + data jsonb not null, + local boolean not null default true, + published timestamp not null default now(), + updated timestamp +); + +-- Making sure that id is unique +create unique index idx_activity_unique_apid on activity ((data ->> 'id'::text)); + +-- Add federation columns to the two actor tables +alter table user_ +-- TODO uniqueness constraints should be added on these 3 columns later +add column actor_id character varying(255) not null default 'changeme', -- This needs to be checked and updated in code, building from the site url if local +add column bio text, -- not on community, already has description +add column local boolean not null default true, +add column private_key text, -- These need to be generated from code +add column public_key text, +add column last_refreshed_at timestamp not null default now() -- Used to re-fetch federated actor periodically +; + +-- Community +alter table community +add column actor_id character varying(255) not null default 'changeme', -- This needs to be checked and updated in code, building from the site url if local +add column local boolean not null default true, +add column private_key text, -- These need to be generated from code +add column public_key text, +add column last_refreshed_at timestamp not null default now() -- Used to re-fetch federated actor periodically +; + +-- Don't worry about rebuilding the views right now. + diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 2bc6e3571..d0c4b8d3e 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -1,5 +1,6 @@ use super::*; use crate::apub::puller::{get_all_communities, get_remote_community}; +use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType}; use crate::settings::Settings; use diesel::PgConnection; use std::str::FromStr; @@ -208,6 +209,8 @@ impl Perform for Oper { } // When you create a community, make sure the user becomes a moderator and a follower + let (community_public_key, community_private_key) = gen_keypair_str(); + let community_form = CommunityForm { name: data.name.to_owned(), title: data.title.to_owned(), @@ -218,6 +221,11 @@ impl Perform for Oper { deleted: None, nsfw: data.nsfw, updated: None, + actor_id: make_apub_endpoint(EndpointType::Community, &data.name).to_string(), + local: true, + private_key: Some(community_private_key), + public_key: Some(community_public_key), + last_refreshed_at: None, }; let inserted_community = match Community::create(&conn, &community_form) { @@ -298,6 +306,8 @@ impl Perform for Oper { return Err(APIError::err("no_community_edit_allowed").into()); } + let read_community = Community::read(&conn, data.edit_id)?; + let community_form = CommunityForm { name: data.name.to_owned(), title: data.title.to_owned(), @@ -308,6 +318,11 @@ impl Perform for Oper { deleted: data.deleted.to_owned(), nsfw: data.nsfw, updated: Some(naive_now()), + actor_id: read_community.actor_id, + local: read_community.local, + private_key: read_community.private_key, + public_key: read_community.public_key, + last_refreshed_at: None, }; let _updated_community = match Community::update(&conn, data.edit_id, &community_form) { @@ -571,6 +586,11 @@ impl Perform for Oper { deleted: None, nsfw: read_community.nsfw, updated: Some(naive_now()), + actor_id: read_community.actor_id, + local: read_community.local, + private_key: read_community.private_key, + public_key: read_community.public_key, + last_refreshed_at: None, }; let _updated_community = match Community::update(&conn, data.community_id, &community_form) { diff --git a/server/src/api/site.rs b/server/src/api/site.rs index ef1a28287..6bd90149b 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -1,5 +1,9 @@ use super::*; +use crate::api::user::Register; +use crate::api::{Oper, Perform}; +use crate::settings::Settings; use diesel::PgConnection; +use log::info; use std::str::FromStr; #[derive(Serialize, Deserialize)] @@ -53,12 +57,12 @@ pub struct GetModlogResponse { #[derive(Serialize, Deserialize)] pub struct CreateSite { - name: String, - description: Option, - enable_downvotes: bool, - open_registration: bool, - enable_nsfw: bool, - auth: String, + pub name: String, + pub description: Option, + pub enable_downvotes: bool, + pub open_registration: bool, + pub enable_nsfw: bool, + pub auth: String, } #[derive(Serialize, Deserialize)] @@ -277,10 +281,34 @@ impl Perform for Oper { fn perform(&self, conn: &PgConnection) -> Result { let _data: &GetSite = &self.data; - // It can return a null site in order to redirect - let site_view = match Site::read(&conn, 1) { - Ok(_site) => Some(SiteView::read(&conn)?), - Err(_e) => None, + let site = Site::read(&conn, 1); + let site_view = if site.is_ok() { + Some(SiteView::read(&conn)?) + } else if let Some(setup) = Settings::get().setup.as_ref() { + let register = Register { + username: setup.admin_username.to_owned(), + email: setup.admin_email.to_owned(), + password: setup.admin_password.to_owned(), + password_verify: setup.admin_password.to_owned(), + admin: true, + show_nsfw: true, + }; + let login_response = Oper::new(register).perform(&conn)?; + info!("Admin {} created", setup.admin_username); + + let create_site = CreateSite { + name: setup.site_name.to_owned(), + description: None, + enable_downvotes: false, + open_registration: false, + enable_nsfw: false, + auth: login_response.jwt, + }; + Oper::new(create_site).perform(&conn)?; + info!("Site {} created", setup.site_name); + Some(SiteView::read(&conn)?) + } else { + None }; let mut admins = UserView::admins(&conn)?; diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 333fd9494..59a3d623a 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -1,4 +1,5 @@ use super::*; +use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType}; use crate::settings::Settings; use crate::{generate_random_string, send_email}; use bcrypt::verify; @@ -14,12 +15,12 @@ pub struct Login { #[derive(Serialize, Deserialize)] pub struct Register { - username: String, - email: Option, - password: String, - password_verify: String, - admin: bool, - show_nsfw: bool, + pub username: String, + pub email: Option, + pub password: String, + pub password_verify: String, + pub admin: bool, + pub show_nsfw: bool, } #[derive(Serialize, Deserialize)] @@ -42,7 +43,7 @@ pub struct SaveUserSettings { #[derive(Serialize, Deserialize)] pub struct LoginResponse { - jwt: String, + pub jwt: String, } #[derive(Serialize, Deserialize)] @@ -250,6 +251,8 @@ impl Perform for Oper { return Err(APIError::err("admin_already_created").into()); } + let (user_public_key, user_private_key) = gen_keypair_str(); + // Register the new user let user_form = UserForm { name: data.username.to_owned(), @@ -269,6 +272,12 @@ impl Perform for Oper { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: make_apub_endpoint(EndpointType::User, &data.username).to_string(), + bio: None, + local: true, + private_key: Some(user_private_key), + public_key: Some(user_public_key), + last_refreshed_at: None, }; // Create the user @@ -287,12 +296,15 @@ impl Perform for Oper { } }; + let (community_public_key, community_private_key) = gen_keypair_str(); + // Create the main community if it doesn't exist let main_community: Community = match Community::read(&conn, 2) { Ok(c) => c, Err(_e) => { + let default_community_name = "main"; let community_form = CommunityForm { - name: "main".to_string(), + name: default_community_name.to_string(), title: "The Default Community".to_string(), description: Some("The Default Community".to_string()), category_id: 1, @@ -301,6 +313,11 @@ impl Perform for Oper { removed: None, deleted: None, updated: None, + actor_id: make_apub_endpoint(EndpointType::Community, default_community_name).to_string(), + local: true, + private_key: Some(community_private_key), + public_key: Some(community_public_key), + last_refreshed_at: None, }; Community::create(&conn, &community_form).unwrap() } @@ -403,6 +420,12 @@ impl Perform for Oper { lang: data.lang.to_owned(), show_avatars: data.show_avatars, send_notifications_to_email: data.send_notifications_to_email, + actor_id: read_user.actor_id, + bio: read_user.bio, + local: read_user.local, + private_key: read_user.private_key, + public_key: read_user.public_key, + last_refreshed_at: None, }; let updated_user = match User_::update(&conn, user_id, &user_form) { @@ -561,6 +584,12 @@ impl Perform for Oper { lang: read_user.lang, show_avatars: read_user.show_avatars, send_notifications_to_email: read_user.send_notifications_to_email, + actor_id: read_user.actor_id, + bio: read_user.bio, + local: read_user.local, + private_key: read_user.private_key, + public_key: read_user.public_key, + last_refreshed_at: None, }; match User_::update(&conn, data.user_id, &user_form) { @@ -624,6 +653,12 @@ impl Perform for Oper { lang: read_user.lang, show_avatars: read_user.show_avatars, send_notifications_to_email: read_user.send_notifications_to_email, + actor_id: read_user.actor_id, + bio: read_user.bio, + local: read_user.local, + private_key: read_user.private_key, + public_key: read_user.public_key, + last_refreshed_at: None, }; match User_::update(&conn, data.user_id, &user_form) { diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 8e7258056..8c915f710 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -3,6 +3,7 @@ pub mod post; pub mod puller; pub mod user; use crate::Settings; +use openssl::{pkey::PKey, rsa::Rsa}; use activitystreams::actor::{properties::ApActorProperties, Group}; use activitystreams::ext::Ext; @@ -21,13 +22,13 @@ where .json(json) } -enum EndpointType { +pub enum EndpointType { Community, User, Post, } -fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url { +pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url { let point = match endpoint_type { EndpointType::Community => "c", EndpointType::User => "u", @@ -51,3 +52,25 @@ pub fn get_apub_protocol_string() -> &'static str { "http" } } + +pub fn gen_keypair() -> (Vec, Vec) { + let rsa = Rsa::generate(2048).expect("sign::gen_keypair: key generation error"); + let pkey = PKey::from_rsa(rsa).expect("sign::gen_keypair: parsing error"); + ( + pkey + .public_key_to_pem() + .expect("sign::gen_keypair: public key encoding error"), + pkey + .private_key_to_pem_pkcs8() + .expect("sign::gen_keypair: private key encoding error"), + ) +} + +pub fn gen_keypair_str() -> (String, String) { + let (public_key, private_key) = gen_keypair(); + (vec_bytes_to_str(public_key), vec_bytes_to_str(private_key)) +} + +fn vec_bytes_to_str(bytes: Vec) -> String { + String::from_utf8_lossy(&bytes).into_owned() +} diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index c92023ec5..6a81b8d06 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -8,8 +8,8 @@ use crate::settings::Settings; use activitystreams::collection::{OrderedCollection, UnorderedCollection}; use activitystreams::object::Page; use activitystreams::BaseBox; -use chttp::prelude::*; use failure::Error; +use isahc::prelude::*; use log::warn; use serde::Deserialize; @@ -67,7 +67,7 @@ where } // TODO: should cache responses here when we are in production // TODO: this function should return a future - let text = chttp::get(uri)?.text()?; + let text = isahc::get(uri)?.text()?; let res: Response = serde_json::from_str(&text)?; Ok(res) } diff --git a/server/src/db/code_migrations.rs b/server/src/db/code_migrations.rs new file mode 100644 index 000000000..e0c736e13 --- /dev/null +++ b/server/src/db/code_migrations.rs @@ -0,0 +1,101 @@ +// This is for db migrations that require code +use super::community::{Community, CommunityForm}; +use super::user::{UserForm, User_}; +use super::*; +use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType}; +use crate::naive_now; +use log::info; + +pub fn run_advanced_migrations(conn: &PgConnection) -> Result<(), Error> { + user_updates_2020_04_02(conn)?; + community_updates_2020_04_02(conn)?; + + Ok(()) +} + +fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> { + use crate::schema::user_::dsl::*; + + info!("Running user_updates_2020_04_02"); + + // Update the actor_id, private_key, and public_key, last_refreshed_at + let incorrect_users = user_ + .filter(actor_id.eq("changeme")) + .filter(local.eq(true)) + .load::(conn)?; + + for cuser in &incorrect_users { + let (user_public_key, user_private_key) = gen_keypair_str(); + + let form = UserForm { + name: cuser.name.to_owned(), + fedi_name: cuser.fedi_name.to_owned(), + email: cuser.email.to_owned(), + matrix_user_id: cuser.matrix_user_id.to_owned(), + avatar: cuser.avatar.to_owned(), + password_encrypted: cuser.password_encrypted.to_owned(), + preferred_username: cuser.preferred_username.to_owned(), + updated: None, + admin: cuser.admin, + banned: cuser.banned, + show_nsfw: cuser.show_nsfw, + theme: cuser.theme.to_owned(), + default_sort_type: cuser.default_sort_type, + default_listing_type: cuser.default_listing_type, + lang: cuser.lang.to_owned(), + show_avatars: cuser.show_avatars, + send_notifications_to_email: cuser.send_notifications_to_email, + actor_id: make_apub_endpoint(EndpointType::User, &cuser.name).to_string(), + bio: cuser.bio.to_owned(), + local: cuser.local, + private_key: Some(user_private_key), + public_key: Some(user_public_key), + last_refreshed_at: Some(naive_now()), + }; + + User_::update(&conn, cuser.id, &form)?; + } + + info!("{} user rows updated.", incorrect_users.len()); + + Ok(()) +} + +fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), Error> { + use crate::schema::community::dsl::*; + + info!("Running community_updates_2020_04_02"); + + // Update the actor_id, private_key, and public_key, last_refreshed_at + let incorrect_communities = community + .filter(actor_id.eq("changeme")) + .filter(local.eq(true)) + .load::(conn)?; + + for ccommunity in &incorrect_communities { + let (community_public_key, community_private_key) = gen_keypair_str(); + + let form = CommunityForm { + name: ccommunity.name.to_owned(), + title: ccommunity.title.to_owned(), + description: ccommunity.description.to_owned(), + category_id: ccommunity.category_id, + creator_id: ccommunity.creator_id, + removed: None, + deleted: None, + nsfw: ccommunity.nsfw, + updated: None, + actor_id: make_apub_endpoint(EndpointType::Community, &ccommunity.name).to_string(), + local: ccommunity.local, + private_key: Some(community_private_key), + public_key: Some(community_public_key), + last_refreshed_at: Some(naive_now()), + }; + + Community::update(&conn, ccommunity.id, &form)?; + } + + info!("{} community rows updated.", incorrect_communities.len()); + + Ok(()) +} diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs index c9bfbac6c..8110fc5ba 100644 --- a/server/src/db/comment.rs +++ b/server/src/db/comment.rs @@ -186,6 +186,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -200,6 +206,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs index 85b41d826..97c03c536 100644 --- a/server/src/db/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -450,6 +450,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -464,6 +470,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/community.rs b/server/src/db/community.rs index 635009635..ff5782243 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -15,6 +15,11 @@ pub struct Community { pub updated: Option, pub deleted: bool, pub nsfw: bool, + pub actor_id: String, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: chrono::NaiveDateTime, } #[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)] @@ -29,6 +34,11 @@ pub struct CommunityForm { pub updated: Option, pub deleted: Option, pub nsfw: bool, + pub actor_id: String, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: Option, } impl Crud for Community { @@ -232,6 +242,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -246,6 +262,11 @@ mod tests { removed: None, deleted: None, updated: None, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); @@ -262,6 +283,11 @@ mod tests { deleted: false, published: inserted_community.published, updated: None, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: inserted_community.published, }; let community_follower_form = CommunityFollowerForm { diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs index dacdb6f6a..f434f7288 100644 --- a/server/src/db/mod.rs +++ b/server/src/db/mod.rs @@ -1,4 +1,3 @@ -extern crate lazy_static; use crate::settings::Settings; use diesel::dsl::*; use diesel::result::Error; @@ -6,6 +5,7 @@ use diesel::*; use serde::{Deserialize, Serialize}; pub mod category; +pub mod code_migrations; pub mod comment; pub mod comment_view; pub mod community; diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs index a8c3df4f1..b01dc5cc1 100644 --- a/server/src/db/moderator.rs +++ b/server/src/db/moderator.rs @@ -454,6 +454,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_mod = User_::create(&conn, &new_mod).unwrap(); @@ -476,6 +482,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -490,6 +502,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/password_reset_request.rs b/server/src/db/password_reset_request.rs index 6951fd399..c9d18e1cf 100644 --- a/server/src/db/password_reset_request.rs +++ b/server/src/db/password_reset_request.rs @@ -104,6 +104,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/post.rs b/server/src/db/post.rs index ffde14d3d..bf9a9ad73 100644 --- a/server/src/db/post.rs +++ b/server/src/db/post.rs @@ -207,6 +207,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -221,6 +227,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs index f48f4f680..587ae52b0 100644 --- a/server/src/db/post_view.rs +++ b/server/src/db/post_view.rs @@ -375,6 +375,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -389,6 +395,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/db/private_message.rs b/server/src/db/private_message.rs index cc073b594..18ec4963b 100644 --- a/server/src/db/private_message.rs +++ b/server/src/db/private_message.rs @@ -81,6 +81,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_creator = User_::create(&conn, &creator_form).unwrap(); @@ -103,6 +109,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_recipient = User_::create(&conn, &recipient_form).unwrap(); diff --git a/server/src/db/user.rs b/server/src/db/user.rs index e7c769650..32596c270 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -27,6 +27,12 @@ pub struct User_ { pub show_avatars: bool, pub send_notifications_to_email: bool, pub matrix_user_id: Option, + pub actor_id: String, + pub bio: Option, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: chrono::NaiveDateTime, } #[derive(Insertable, AsChangeset, Clone)] @@ -49,6 +55,12 @@ pub struct UserForm { pub show_avatars: bool, pub send_notifications_to_email: bool, pub matrix_user_id: Option, + pub actor_id: String, + pub bio: Option, + pub local: bool, + pub private_key: Option, + pub public_key: Option, + pub last_refreshed_at: Option, } impl Crud for User_ { @@ -78,6 +90,7 @@ impl User_ { Self::create(&conn, &edited_user) } + // TODO do more individual updates like these pub fn update_password( conn: &PgConnection, user_id: i32, @@ -202,6 +215,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -226,6 +245,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: inserted_user.published, }; let read_user = User_::read(&conn, inserted_user.id).unwrap(); diff --git a/server/src/db/user_mention.rs b/server/src/db/user_mention.rs index 0cf257955..48814c89a 100644 --- a/server/src/db/user_mention.rs +++ b/server/src/db/user_mention.rs @@ -80,6 +80,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -102,6 +108,12 @@ mod tests { lang: "browser".into(), show_avatars: true, send_notifications_to_email: false, + actor_id: "changeme".into(), + bio: None, + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_recipient = User_::create(&conn, &recipient_form).unwrap(); @@ -116,6 +128,11 @@ mod tests { deleted: None, updated: None, nsfw: false, + actor_id: "changeme".into(), + local: true, + private_key: None, + public_key: None, + last_refreshed_at: None, }; let inserted_community = Community::create(&conn, &new_community).unwrap(); diff --git a/server/src/lib.rs b/server/src/lib.rs index 2507819dc..e45311eec 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -11,13 +11,16 @@ pub extern crate actix; pub extern crate actix_web; pub extern crate bcrypt; pub extern crate chrono; +pub extern crate comrak; pub extern crate dotenv; pub extern crate jsonwebtoken; pub extern crate lettre; pub extern crate lettre_email; extern crate log; +pub extern crate openssl; pub extern crate rand; pub extern crate regex; +pub extern crate rss; pub extern crate serde; pub extern crate serde_json; pub extern crate sha2; @@ -34,7 +37,7 @@ pub mod websocket; use crate::settings::Settings; use chrono::{DateTime, FixedOffset, Local, NaiveDateTime}; -use chttp::prelude::*; +use isahc::prelude::*; use lettre::smtp::authentication::{Credentials, Mechanism}; use lettre::smtp::extension::ClientId; use lettre::smtp::ConnectionReuseParameters; @@ -159,7 +162,7 @@ pub struct IframelyResponse { pub fn fetch_iframely(url: &str) -> Result { let fetch_url = format!("http://iframely/oembed?url={}", url); - let text = chttp::get(&fetch_url)?.text()?; + let text = isahc::get(&fetch_url)?.text()?; let res: IframelyResponse = serde_json::from_str(&text)?; Ok(res) } @@ -175,7 +178,7 @@ pub fn fetch_pictshare(image_url: &str) -> Result String { + comrak::markdown_to_html(text, &comrak::ComrakOptions::default()) +} + #[cfg(test)] mod tests { use crate::{extract_usernames, is_email_regex, remove_slurs, slur_check, slurs_vec_to_str}; diff --git a/server/src/main.rs b/server/src/main.rs index 601c2e0dc..f2a19d30b 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -6,6 +6,7 @@ use actix::prelude::*; use actix_web::*; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; +use lemmy_server::db::code_migrations::run_advanced_migrations; use lemmy_server::routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket}; use lemmy_server::settings::Settings; use lemmy_server::websocket::server::*; @@ -28,6 +29,7 @@ async fn main() -> io::Result<()> { // Run the migrations from code let conn = pool.get().unwrap(); embedded_migrations::run(&conn).unwrap(); + run_advanced_migrations(&conn).unwrap(); // Set up websocket server let server = ChatServer::startup(pool.clone()).start(); diff --git a/server/src/routes/feeds.rs b/server/src/routes/feeds.rs index ad0f28d5a..6826cf5cb 100644 --- a/server/src/routes/feeds.rs +++ b/server/src/routes/feeds.rs @@ -1,5 +1,3 @@ -extern crate rss; - use super::*; use crate::db::comment_view::{ReplyQueryBuilder, ReplyView}; use crate::db::community::Community; @@ -8,9 +6,9 @@ use crate::db::site_view::SiteView; use crate::db::user::{Claims, User_}; use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView}; use crate::db::{ListingType, SortType}; -use crate::Settings; +use crate::{markdown_to_html, Settings}; use actix_web::{web, HttpResponse, Result}; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, NaiveDateTime, Utc}; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::PgConnection; use failure::Error; @@ -34,7 +32,6 @@ enum RequestType { pub fn config(cfg: &mut web::ServiceConfig) { cfg .route("/feeds/{type}/{name}.xml", web::get().to(feeds::get_feed)) - .route("/feeds/all.xml", web::get().to(feeds::get_all_feed)) .route("/feeds/all.xml", web::get().to(feeds::get_all_feed)); } @@ -44,9 +41,7 @@ async fn get_all_feed( ) -> Result { let res = web::block(move || { let conn = db.get()?; - - let sort_type = get_sort_type(info)?; - get_feed_all_data(&conn, &sort_type) + get_feed_all_data(&conn, &get_sort_type(info)?) }) .await .map(|rss| { @@ -58,6 +53,29 @@ async fn get_all_feed( Ok(res) } +fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result { + let site_view = SiteView::read(&conn)?; + + let posts = PostQueryBuilder::create(&conn) + .listing_type(ListingType::All) + .sort(sort_type) + .list()?; + + let items = create_post_items(posts); + + let mut channel_builder = ChannelBuilder::default(); + channel_builder + .title(&format!("{} - All", site_view.name)) + .link(format!("https://{}", Settings::get().hostname)) + .items(items); + + if let Some(site_desc) = site_view.description { + channel_builder.description(&site_desc); + } + + Ok(channel_builder.build().unwrap().to_string()) +} + async fn get_feed( path: web::Path<(String, String)>, info: web::Query, @@ -86,6 +104,7 @@ async fn get_feed( } }) .await + .map(|builder| builder.build().unwrap().to_string()) .map(|rss| { HttpResponse::Ok() .content_type("application/rss+xml") @@ -103,34 +122,11 @@ fn get_sort_type(info: web::Query) -> Result { SortType::from_str(&sort_query) } -fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result { - let site_view = SiteView::read(&conn)?; - - let posts = PostQueryBuilder::create(&conn) - .listing_type(ListingType::All) - .sort(sort_type) - .list()?; - - let items = create_post_items(posts); - - let mut channel_builder = ChannelBuilder::default(); - channel_builder - .title(&format!("{} - All", site_view.name)) - .link(format!("https://{}", Settings::get().hostname)) - .items(items); - - if let Some(site_desc) = site_view.description { - channel_builder.description(&site_desc); - } - - Ok(channel_builder.build().unwrap().to_string()) -} - fn get_feed_user( conn: &PgConnection, sort_type: &SortType, user_name: String, -) -> Result { +) -> Result { let site_view = SiteView::read(&conn)?; let user = User_::find_by_username(&conn, &user_name)?; let user_url = user.get_profile_url(); @@ -149,14 +145,14 @@ fn get_feed_user( .link(user_url) .items(items); - Ok(channel_builder.build().unwrap().to_string()) + Ok(channel_builder) } fn get_feed_community( conn: &PgConnection, sort_type: &SortType, community_name: String, -) -> Result { +) -> Result { let site_view = SiteView::read(&conn)?; let community = Community::read_from_name(&conn, community_name)?; let community_url = community.get_url(); @@ -179,10 +175,14 @@ fn get_feed_community( channel_builder.description(&community_desc); } - Ok(channel_builder.build().unwrap().to_string()) + Ok(channel_builder) } -fn get_feed_front(conn: &PgConnection, sort_type: &SortType, jwt: String) -> Result { +fn get_feed_front( + conn: &PgConnection, + sort_type: &SortType, + jwt: String, +) -> Result { let site_view = SiteView::read(&conn)?; let user_id = Claims::decode(&jwt)?.claims.id; @@ -204,10 +204,10 @@ fn get_feed_front(conn: &PgConnection, sort_type: &SortType, jwt: String) -> Res channel_builder.description(&site_desc); } - Ok(channel_builder.build().unwrap().to_string()) + Ok(channel_builder) } -fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result { +fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result { let site_view = SiteView::read(&conn)?; let user_id = Claims::decode(&jwt)?.claims.id; @@ -233,86 +233,61 @@ fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result { channel_builder.description(&site_desc); } - Ok(channel_builder.build().unwrap().to_string()) + Ok(channel_builder) } fn create_reply_and_mention_items( replies: Vec, mentions: Vec, ) -> Vec { - let mut items: Vec = Vec::new(); + let mut reply_items: Vec = replies + .iter() + .map(|r| { + let reply_url = format!( + "https://{}/post/{}/comment/{}", + Settings::get().hostname, + r.post_id, + r.id + ); + build_item(&r.creator_name, &r.published, &reply_url, &r.content) + }) + .collect(); - for r in replies { - let mut i = ItemBuilder::default(); + let mut mention_items: Vec = mentions + .iter() + .map(|m| { + let mention_url = format!( + "https://{}/post/{}/comment/{}", + Settings::get().hostname, + m.post_id, + m.id + ); + build_item(&m.creator_name, &m.published, &mention_url, &m.content) + }) + .collect(); - i.title(format!("Reply from {}", r.creator_name)); + reply_items.append(&mut mention_items); + reply_items +} - let author_url = format!("https://{}/u/{}", Settings::get().hostname, r.creator_name); - i.author(format!( - "/u/{} (link)", - r.creator_name, author_url - )); - - let dt = DateTime::::from_utc(r.published, Utc); - i.pub_date(dt.to_rfc2822()); - - let reply_url = format!( - "https://{}/post/{}/comment/{}", - Settings::get().hostname, - r.post_id, - r.id - ); - i.comments(reply_url.to_owned()); - let guid = GuidBuilder::default() - .permalink(true) - .value(&reply_url) - .build(); - i.guid(guid.unwrap()); - - i.link(reply_url); - - // TODO find a markdown to html parser here, do images, etc - i.description(r.content); - - items.push(i.build().unwrap()); - } - - for m in mentions { - let mut i = ItemBuilder::default(); - - i.title(format!("Mention from {}", m.creator_name)); - - let author_url = format!("https://{}/u/{}", Settings::get().hostname, m.creator_name); - i.author(format!( - "/u/{} (link)", - m.creator_name, author_url - )); - - let dt = DateTime::::from_utc(m.published, Utc); - i.pub_date(dt.to_rfc2822()); - - let mention_url = format!( - "https://{}/post/{}/comment/{}", - Settings::get().hostname, - m.post_id, - m.id - ); - i.comments(mention_url.to_owned()); - let guid = GuidBuilder::default() - .permalink(true) - .value(&mention_url) - .build(); - i.guid(guid.unwrap()); - - i.link(mention_url); - - // TODO find a markdown to html parser here, do images, etc - i.description(m.content); - - items.push(i.build().unwrap()); - } - - items +fn build_item(creator_name: &str, published: &NaiveDateTime, url: &str, content: &str) -> Item { + let mut i = ItemBuilder::default(); + i.title(format!("Reply from {}", creator_name)); + let author_url = format!("https://{}/u/{}", Settings::get().hostname, creator_name); + i.author(format!( + "/u/{} (link)", + creator_name, author_url + )); + let dt = DateTime::::from_utc(*published, Utc); + i.pub_date(dt.to_rfc2822()); + i.comments(url.to_owned()); + let guid = GuidBuilder::default().permalink(true).value(url).build(); + i.guid(guid.unwrap()); + i.link(url.to_owned()); + // TODO add images + let html = markdown_to_html(&content.to_string()); + i.description(html); + i.build().unwrap() } fn create_post_items(posts: Vec) -> Vec { @@ -359,9 +334,8 @@ fn create_post_items(posts: Vec) -> Vec { i.link(url); } - // TODO find a markdown to html parser here, do images, etc - let mut description = format!(" - submitted by {} to {}
{} points | {} comments", + // TODO add images + let mut description = format!("submitted by {} to {}
{} points | {} comments", author_url, p.creator_name, community_url, @@ -371,7 +345,8 @@ fn create_post_items(posts: Vec) -> Vec { p.number_of_comments); if let Some(body) = p.body { - description.push_str(&format!("

{}", body)); + let html = markdown_to_html(&body); + description.push_str(&html); } i.description(description); diff --git a/server/src/routes/nodeinfo.rs b/server/src/routes/nodeinfo.rs index 679837ee1..1fee54a80 100644 --- a/server/src/routes/nodeinfo.rs +++ b/server/src/routes/nodeinfo.rs @@ -1,4 +1,3 @@ -extern crate lazy_static; use crate::apub::get_apub_protocol_string; use crate::db::site_view::SiteView; use crate::version; diff --git a/server/src/schema.rs b/server/src/schema.rs index d9449fef9..41769ded6 100644 --- a/server/src/schema.rs +++ b/server/src/schema.rs @@ -1,316 +1,339 @@ table! { - category (id) { - id -> Int4, - name -> Varchar, - } + activity (id) { + id -> Int4, + user_id -> Int4, + data -> Jsonb, + local -> Bool, + published -> Timestamp, + updated -> Nullable, + } } table! { - comment (id) { - id -> Int4, - creator_id -> Int4, - post_id -> Int4, - parent_id -> Nullable, - content -> Text, - removed -> Bool, - read -> Bool, - published -> Timestamp, - updated -> Nullable, - deleted -> Bool, - } + category (id) { + id -> Int4, + name -> Varchar, + } } table! { - comment_like (id) { - id -> Int4, - user_id -> Int4, - comment_id -> Int4, - post_id -> Int4, - score -> Int2, - published -> Timestamp, - } + comment (id) { + id -> Int4, + creator_id -> Int4, + post_id -> Int4, + parent_id -> Nullable, + content -> Text, + removed -> Bool, + read -> Bool, + published -> Timestamp, + updated -> Nullable, + deleted -> Bool, + } } table! { - comment_saved (id) { - id -> Int4, - comment_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + comment_like (id) { + id -> Int4, + user_id -> Int4, + comment_id -> Int4, + post_id -> Int4, + score -> Int2, + published -> Timestamp, + } } table! { - community (id) { - id -> Int4, - name -> Varchar, - title -> Varchar, - description -> Nullable, - category_id -> Int4, - creator_id -> Int4, - removed -> Bool, - published -> Timestamp, - updated -> Nullable, - deleted -> Bool, - nsfw -> Bool, - } + comment_saved (id) { + id -> Int4, + comment_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - community_follower (id) { - id -> Int4, - community_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + community (id) { + id -> Int4, + name -> Varchar, + title -> Varchar, + description -> Nullable, + category_id -> Int4, + creator_id -> Int4, + removed -> Bool, + published -> Timestamp, + updated -> Nullable, + deleted -> Bool, + nsfw -> Bool, + actor_id -> Varchar, + local -> Bool, + private_key -> Nullable, + public_key -> Nullable, + last_refreshed_at -> Timestamp, + } } table! { - community_moderator (id) { - id -> Int4, - community_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + community_follower (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - community_user_ban (id) { - id -> Int4, - community_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + community_moderator (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - mod_add (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - removed -> Nullable, - when_ -> Timestamp, - } + community_user_ban (id) { + id -> Int4, + community_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - mod_add_community (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - community_id -> Int4, - removed -> Nullable, - when_ -> Timestamp, - } + mod_add (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - mod_ban (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - reason -> Nullable, - banned -> Nullable, - expires -> Nullable, - when_ -> Timestamp, - } + mod_add_community (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - mod_ban_from_community (id) { - id -> Int4, - mod_user_id -> Int4, - other_user_id -> Int4, - community_id -> Int4, - reason -> Nullable, - banned -> Nullable, - expires -> Nullable, - when_ -> Timestamp, - } + mod_ban (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } } table! { - mod_lock_post (id) { - id -> Int4, - mod_user_id -> Int4, - post_id -> Int4, - locked -> Nullable, - when_ -> Timestamp, - } + mod_ban_from_community (id) { + id -> Int4, + mod_user_id -> Int4, + other_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + banned -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } } table! { - mod_remove_comment (id) { - id -> Int4, - mod_user_id -> Int4, - comment_id -> Int4, - reason -> Nullable, - removed -> Nullable, - when_ -> Timestamp, - } + mod_lock_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + locked -> Nullable, + when_ -> Timestamp, + } } table! { - mod_remove_community (id) { - id -> Int4, - mod_user_id -> Int4, - community_id -> Int4, - reason -> Nullable, - removed -> Nullable, - expires -> Nullable, - when_ -> Timestamp, - } + mod_remove_comment (id) { + id -> Int4, + mod_user_id -> Int4, + comment_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - mod_remove_post (id) { - id -> Int4, - mod_user_id -> Int4, - post_id -> Int4, - reason -> Nullable, - removed -> Nullable, - when_ -> Timestamp, - } + mod_remove_community (id) { + id -> Int4, + mod_user_id -> Int4, + community_id -> Int4, + reason -> Nullable, + removed -> Nullable, + expires -> Nullable, + when_ -> Timestamp, + } } table! { - mod_sticky_post (id) { - id -> Int4, - mod_user_id -> Int4, - post_id -> Int4, - stickied -> Nullable, - when_ -> Timestamp, - } + mod_remove_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + reason -> Nullable, + removed -> Nullable, + when_ -> Timestamp, + } } table! { - password_reset_request (id) { - id -> Int4, - user_id -> Int4, - token_encrypted -> Text, - published -> Timestamp, - } + mod_sticky_post (id) { + id -> Int4, + mod_user_id -> Int4, + post_id -> Int4, + stickied -> Nullable, + when_ -> Timestamp, + } } table! { - post (id) { - id -> Int4, - name -> Varchar, - url -> Nullable, - body -> Nullable, - creator_id -> Int4, - community_id -> Int4, - removed -> Bool, - locked -> Bool, - published -> Timestamp, - updated -> Nullable, - deleted -> Bool, - nsfw -> Bool, - stickied -> Bool, - embed_title -> Nullable, - embed_description -> Nullable, - embed_html -> Nullable, - thumbnail_url -> Nullable, - } + password_reset_request (id) { + id -> Int4, + user_id -> Int4, + token_encrypted -> Text, + published -> Timestamp, + } } table! { - post_like (id) { - id -> Int4, - post_id -> Int4, - user_id -> Int4, - score -> Int2, - published -> Timestamp, - } + post (id) { + id -> Int4, + name -> Varchar, + url -> Nullable, + body -> Nullable, + creator_id -> Int4, + community_id -> Int4, + removed -> Bool, + locked -> Bool, + published -> Timestamp, + updated -> Nullable, + deleted -> Bool, + nsfw -> Bool, + stickied -> Bool, + embed_title -> Nullable, + embed_description -> Nullable, + embed_html -> Nullable, + thumbnail_url -> Nullable, + } } table! { - post_read (id) { - id -> Int4, - post_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + post_like (id) { + id -> Int4, + post_id -> Int4, + user_id -> Int4, + score -> Int2, + published -> Timestamp, + } } table! { - post_saved (id) { - id -> Int4, - post_id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + post_read (id) { + id -> Int4, + post_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - private_message (id) { - id -> Int4, - creator_id -> Int4, - recipient_id -> Int4, - content -> Text, - deleted -> Bool, - read -> Bool, - published -> Timestamp, - updated -> Nullable, - } + post_saved (id) { + id -> Int4, + post_id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } table! { - site (id) { - id -> Int4, - name -> Varchar, - description -> Nullable, - creator_id -> Int4, - published -> Timestamp, - updated -> Nullable, - enable_downvotes -> Bool, - open_registration -> Bool, - enable_nsfw -> Bool, - } + private_message (id) { + id -> Int4, + creator_id -> Int4, + recipient_id -> Int4, + content -> Text, + deleted -> Bool, + read -> Bool, + published -> Timestamp, + updated -> Nullable, + } } table! { - user_ (id) { - id -> Int4, - name -> Varchar, - fedi_name -> Varchar, - preferred_username -> Nullable, - password_encrypted -> Text, - email -> Nullable, - avatar -> Nullable, - admin -> Bool, - banned -> Bool, - published -> Timestamp, - updated -> Nullable, - show_nsfw -> Bool, - theme -> Varchar, - default_sort_type -> Int2, - default_listing_type -> Int2, - lang -> Varchar, - show_avatars -> Bool, - send_notifications_to_email -> Bool, - matrix_user_id -> Nullable, - } + site (id) { + id -> Int4, + name -> Varchar, + description -> Nullable, + creator_id -> Int4, + published -> Timestamp, + updated -> Nullable, + enable_downvotes -> Bool, + open_registration -> Bool, + enable_nsfw -> Bool, + } } table! { - user_ban (id) { - id -> Int4, - user_id -> Int4, - published -> Timestamp, - } + user_ (id) { + id -> Int4, + name -> Varchar, + fedi_name -> Varchar, + preferred_username -> Nullable, + password_encrypted -> Text, + email -> Nullable, + avatar -> Nullable, + admin -> Bool, + banned -> Bool, + published -> Timestamp, + updated -> Nullable, + show_nsfw -> Bool, + theme -> Varchar, + default_sort_type -> Int2, + default_listing_type -> Int2, + lang -> Varchar, + show_avatars -> Bool, + send_notifications_to_email -> Bool, + matrix_user_id -> Nullable, + actor_id -> Varchar, + bio -> Nullable, + local -> Bool, + private_key -> Nullable, + public_key -> Nullable, + last_refreshed_at -> Timestamp, + } } table! { - user_mention (id) { - id -> Int4, - recipient_id -> Int4, - comment_id -> Int4, - read -> Bool, - published -> Timestamp, - } + user_ban (id) { + id -> Int4, + user_id -> Int4, + published -> Timestamp, + } } +table! { + user_mention (id) { + id -> Int4, + recipient_id -> Int4, + comment_id -> Int4, + read -> Bool, + published -> Timestamp, + } +} + +joinable!(activity -> user_ (user_id)); joinable!(comment -> post (post_id)); joinable!(comment -> user_ (creator_id)); joinable!(comment_like -> comment (comment_id)); @@ -353,6 +376,7 @@ joinable!(user_mention -> comment (comment_id)); joinable!(user_mention -> user_ (recipient_id)); allow_tables_to_appear_in_same_query!( + activity, category, comment, comment_like, diff --git a/server/src/settings.rs b/server/src/settings.rs index 3928f74e1..29d5966ba 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -1,4 +1,3 @@ -extern crate lazy_static; use config::{Config, ConfigError, Environment, File}; use serde::Deserialize; use std::env; @@ -9,6 +8,7 @@ static CONFIG_FILE: &str = "config/config.hjson"; #[derive(Debug, Deserialize)] pub struct Settings { + pub setup: Option, pub database: Database, pub hostname: String, pub bind: IpAddr, @@ -20,6 +20,14 @@ pub struct Settings { pub federation: Federation, } +#[derive(Debug, Deserialize)] +pub struct Setup { + pub admin_username: String, + pub admin_password: String, + pub admin_email: Option, + pub site_name: String, +} + #[derive(Debug, Deserialize)] pub struct RateLimitConfig { pub message: i32, diff --git a/server/src/version.rs b/server/src/version.rs index b7bdab094..1491a0a2c 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.39"; +pub const VERSION: &str = "v0.6.44"; diff --git a/ui/.eslintrc.json b/ui/.eslintrc.json index bee9e538f..a4aecfa01 100644 --- a/ui/.eslintrc.json +++ b/ui/.eslintrc.json @@ -38,6 +38,7 @@ "inferno/no-direct-mutation-state": 0, "inferno/no-unknown-property": 0, "max-statements": 0, + "max-params": 0, "new-cap": 0, "no-console": 0, "no-duplicate-imports": 0, diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css index 618efa40d..1c8206e35 100644 --- a/ui/assets/css/main.css +++ b/ui/assets/css/main.css @@ -152,7 +152,7 @@ svg.thumbnail { } hr { - border-top: 1px solid var(--secondary); + border-top: 1px solid var(--light); } .emoji { @@ -186,7 +186,7 @@ hr { max-height: 90vh; } -.vote-animate:active { +.btn-animate:active { transform: scale(1.2); -webkit-transform: scale(1.2); -ms-transform: scale(1.2); diff --git a/ui/package.json b/ui/package.json index e3cabae5b..7d946614c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,18 +15,18 @@ "keywords": [], "dependencies": { "@types/autosize": "^3.0.6", - "@types/js-cookie": "^2.2.1", + "@types/js-cookie": "^2.2.5", "@types/jwt-decode": "^2.2.1", "@types/markdown-it": "^0.0.9", "@types/markdown-it-container": "^2.0.2", - "@types/node": "^13.7.0", + "@types/node": "^13.9.2", "autosize": "^4.0.2", "bootswatch": "^4.3.1", "classcat": "^1.1.3", "dotenv": "^8.2.0", "emoji-short-name": "^1.0.0", - "husky": "^4.2.1", - "i18next": "^19.0.3", + "husky": "^4.2.3", + "i18next": "^19.3.3", "inferno": "^7.4.2", "inferno-i18next": "nimbusec-oss/inferno-i18next", "inferno-router": "^7.4.2", @@ -40,21 +40,21 @@ "prettier": "^1.18.2", "reconnecting-websocket": "^4.4.0", "rxjs": "^6.4.0", - "terser": "^4.6.3", - "tippy.js": "^6.0.0", - "toastify-js": "^1.6.2", - "tributejs": "^5.0.0", + "terser": "^4.6.7", + "tippy.js": "^6.1.0", + "toastify-js": "^1.7.0", + "tributejs": "^5.1.2", "twemoji": "^12.1.2", - "ws": "^7.0.0" + "ws": "^7.2.3" }, "devDependencies": { "eslint": "^6.5.1", "eslint-plugin-inferno": "^7.14.3", - "eslint-plugin-jane": "^7.0.2", + "eslint-plugin-jane": "^7.2.0", "fuse-box": "^3.1.3", - "lint-staged": "^10.0.2", - "sortpack": "^2.0.1", - "ts-node": "^8.6.2", + "lint-staged": "^10.0.8", + "sortpack": "^2.1.2", + "ts-node": "^8.7.0", "ts-transform-classcat": "^0.0.2", "ts-transform-inferno": "^4.0.2", "typescript": "^3.8.3" diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 32e43fdc7..39f29b5f8 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -56,6 +56,8 @@ interface CommentNodeState { upvotes: number; downvotes: number; borderColor: string; + readLoading: boolean; + saveLoading: boolean; } interface CommentNodeProps { @@ -64,6 +66,7 @@ interface CommentNodeProps { viewOnly?: boolean; locked?: boolean; markable?: boolean; + showContext?: boolean; moderators: Array; admins: Array; // TODO is this necessary, can't I get it from the node itself? @@ -97,6 +100,8 @@ export class CommentNode extends Component { borderColor: this.props.node.comment.depth ? colorList[this.props.node.comment.depth % colorList.length] : colorList[0], + readLoading: false, + saveLoading: false, }; constructor(props: any, context: any) { @@ -113,6 +118,8 @@ export class CommentNode extends Component { this.state.upvotes = nextProps.node.comment.upvotes; this.state.downvotes = nextProps.node.comment.downvotes; this.state.score = nextProps.node.comment.score; + this.state.readLoading = false; + this.state.saveLoading = false; this.setState(this.state); } @@ -121,18 +128,12 @@ export class CommentNode extends Component { return (
- {!node.comment.parent_id && !this.props.noIndent && ( - <> -
-
- - )}
{ this.props.node.comment.parent_id && 'ml-2'}`} > -
    -
  • - - {node.comment.creator_avatar && showAvatars() && ( - - )} - {node.comment.creator_name} - -
  • +
    + + {node.comment.creator_avatar && showAvatars() && ( + + )} + {node.comment.creator_name} + {this.isMod && ( -
  • +
    {i18n.t('mod')} -
  • +
    )} {this.isAdmin && ( -
  • +
    {i18n.t('admin')} -
  • +
)} {this.isPostCreator && ( -
  • +
    {i18n.t('creator')} -
  • +
    )} {(node.comment.banned_from_community || node.comment.banned) && ( -
  • +
    {i18n.t('banned')} -
  • +
    )} {this.props.showCommunity && ( -
  • - {i18n.t('to')} - + <> + {i18n.t('to')} + {node.comment.community_name} -
  • + )} -
  • -
  • - - - +
    + {this.state.collapsed ? ( + + - {this.state.score} - -
  • -
  • -
  • - - - -
  • -
  • -
    - {this.state.collapsed ? ( - - - - ) : ( - - - - )} -
    -
  • - + ) : ( + + + + )} + + + + + + {this.state.score} + + + + + + + {/* end of user row */} {this.state.showEdit && ( { )} /> )} -
      +
      + {this.props.showContext && this.linkBtn} {this.props.markable && ( -
    • - +
    • + )} + )} {UserService.Instance.user && !this.props.viewOnly && ( <> -
    • + + {WebSocketService.Instance.site.enable_downvotes && ( -
    • - {WebSocketService.Instance.site.enable_downvotes && ( -
    • - -
    • )} -
    • - - - - - -
    • -
    • - - - - - -
    • - {!this.state.showAdvanced ? ( -
    • - + {this.state.saveLoading ? ( + this.loadingIcon + ) : ( + - - - - -
    • + + + )} + + + {!this.state.showAdvanced ? ( + ) : ( <> {!this.myComment && ( -
    • +
    • + )} -
    • - + - - - - -
    • -
    • - - - - - -
    • + + + {this.myComment && ( <> -
    • -
    • - + + + + +
    • -
    • - - - - - -
    • + + + )} {/* Admins and mods can remove comments */} {(this.canMod || this.canAdmin) && ( <> -
    • - {!node.comment.removed ? ( - - {i18n.t('remove')} - - ) : ( - - {i18n.t('restore')} - - )} -
    • + {!node.comment.removed ? ( + + ) : ( + + )} )} {/* Mods can ban from community, and appoint as mods to community */} {this.canMod && ( <> - {!this.isMod && ( -
    • - {!node.comment.banned_from_community ? ( - + {i18n.t('ban')} + + ) : ( + + ))} + {!node.comment.banned_from_community && + (!this.state.showConfirmAppointAsMod ? ( + + ) : ( + <> + +
    • - )} - {!node.comment.banned_from_community && ( -
    • - {!this.state.showConfirmAppointAsMod ? ( - - {this.isMod - ? i18n.t('remove_as_mod') - : i18n.t('appoint_as_mod')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
    • - )} + {i18n.t('no')} + + + ))} )} {/* Community creators and admins can transfer community to another mod */} {(this.amCommunityCreator || this.canAdmin) && - this.isMod && ( -
    • - {!this.state.showConfirmTransferCommunity ? ( - - {i18n.t('transfer_community')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - + this.isMod && + (!this.state.showConfirmTransferCommunity ? ( + + ) : ( + <> + + + + + ))} {/* Admins can ban from all, and appoint other admins */} {this.canAdmin && ( <> - {!this.isAdmin && ( -
    • - {!node.comment.banned ? ( - + {i18n.t('ban_from_site')} + + ) : ( + + ))} + {!node.comment.banned && + (!this.state.showConfirmAppointAsAdmin ? ( + + ) : ( + <> + +
    • - )} - {!node.comment.banned && ( -
    • - {!this.state.showConfirmAppointAsAdmin ? ( - - {this.isAdmin - ? i18n.t('remove_as_admin') - : i18n.t('appoint_as_admin')} - - ) : ( - <> - - {i18n.t('are_you_sure')} - - - {i18n.t('yes')} - - - {i18n.t('no')} - - - )} -
    • - )} + {i18n.t('no')} + + + ))} )} {/* Site Creator can transfer to another admin */} - {this.amSiteCreator && this.isAdmin && ( -
    • - {!this.state.showConfirmTransferSite ? ( - + {i18n.t('transfer_site')} + + ) : ( + <> + +
    • - )} + {i18n.t('yes')} + + + + ))} )} )} -
    + + {/* end of button group */} )} @@ -762,6 +704,33 @@ export class CommentNode extends Component { ); } + get linkBtn() { + let node = this.props.node; + return ( + + ); + } + + get loadingIcon() { + return ( + + + + ); + } + get myComment(): boolean { return ( UserService.Instance.user && @@ -881,6 +850,9 @@ export class CommentNode extends Component { }; WebSocketService.Instance.saveComment(form); + + i.state.saveLoading = true; + i.setState(this.state); } handleReplyCancel() { @@ -993,6 +965,9 @@ export class CommentNode extends Component { }; WebSocketService.Instance.editComment(form); } + + i.state.readLoading = true; + i.setState(this.state); } handleModBanFromCommunityShow(i: CommentNode) { diff --git a/ui/src/components/comment-nodes.tsx b/ui/src/components/comment-nodes.tsx index ebbef8fad..875db1f2a 100644 --- a/ui/src/components/comment-nodes.tsx +++ b/ui/src/components/comment-nodes.tsx @@ -20,6 +20,7 @@ interface CommentNodesProps { viewOnly?: boolean; locked?: boolean; markable?: boolean; + showContext?: boolean; showCommunity?: boolean; sort?: CommentSortType; sortType?: SortType; @@ -47,6 +48,7 @@ export class CommentNodes extends Component< admins={this.props.admins} postCreatorId={this.props.postCreatorId} markable={this.props.markable} + showContext={this.props.showContext} showCommunity={this.props.showCommunity} sort={this.props.sort} sortType={this.props.sortType} diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 4e8e9d1b6..a921de2c1 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -189,6 +189,7 @@ export class Community extends Component { nodes={commentsToFlatNodes(this.state.comments)} noIndent sortType={this.state.sort} + showContext /> ); } diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index afd9bf95f..4fa1498ac 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -1,5 +1,4 @@ import { Component, linkEvent } from 'inferno'; -import { Link } from 'inferno-router'; import { Subscription } from 'rxjs'; import { retryWhen, delay, take } from 'rxjs/operators'; import { @@ -34,14 +33,13 @@ import { CommentNodes } from './comment-nodes'; import { PrivateMessage } from './private-message'; import { SortSelect } from './sort-select'; import { i18n } from '../i18next'; -import { T } from 'inferno-i18next'; enum UnreadOrAll { Unread, All, } -enum UnreadType { +enum MessageType { All, Replies, Mentions, @@ -52,7 +50,7 @@ type ReplyType = Comment | PrivateMessageI; interface InboxState { unreadOrAll: UnreadOrAll; - unreadType: UnreadType; + messageType: MessageType; replies: Array; mentions: Array; messages: Array; @@ -64,7 +62,7 @@ export class Inbox extends Component { private subscription: Subscription; private emptyState: InboxState = { unreadOrAll: UnreadOrAll.Unread, - unreadType: UnreadType.All, + messageType: MessageType.All, replies: [], mentions: [], messages: [], @@ -100,26 +98,19 @@ export class Inbox extends Component { } render() { - let user = UserService.Instance.user; return (
    -
    - - ## - +
    + {i18n.t('inbox')} - + # @@ -139,10 +130,10 @@ export class Inbox extends Component { )} {this.selects()} - {this.state.unreadType == UnreadType.All && this.all()} - {this.state.unreadType == UnreadType.Replies && this.replies()} - {this.state.unreadType == UnreadType.Mentions && this.mentions()} - {this.state.unreadType == UnreadType.Messages && this.messages()} + {this.state.messageType == MessageType.All && this.all()} + {this.state.messageType == MessageType.Replies && this.replies()} + {this.state.messageType == MessageType.Mentions && this.mentions()} + {this.state.messageType == MessageType.Messages && this.messages()} {this.paginator()}
    @@ -150,29 +141,103 @@ export class Inbox extends Component { ); } + unreadOrAllRadios() { + return ( +
    + + +
    + ); + } + + messageTypeRadios() { + return ( +
    + + + + +
    + ); + } + selects() { return (
    - - + {this.unreadOrAllRadios()} + {this.messageTypeRadios()} {
    {combined.map(i => isCommentType(i) ? ( - + ) : ( ) @@ -212,6 +282,7 @@ export class Inbox extends Component { nodes={commentsToFlatNodes(this.state.replies)} noIndent markable + showContext />
    ); @@ -221,7 +292,12 @@ export class Inbox extends Component { return (
    {this.state.mentions.map(mention => ( - + ))}
    ); @@ -277,8 +353,8 @@ export class Inbox extends Component { i.refetch(); } - handleUnreadTypeChange(i: Inbox, event: any) { - i.state.unreadType = Number(event.target.value); + handleMessageTypeChange(i: Inbox, event: any) { + i.state.messageType = Number(event.target.value); i.state.page = 1; i.setState(i.state); i.refetch(); diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 51b31ced9..380033127 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -423,6 +423,7 @@ export class Main extends Component { noIndent showCommunity sortType={this.state.sort} + showContext /> ); } diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index ef3f84309..d7f3b5a8a 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -26,6 +26,8 @@ import { fetchLimit, isCommentType, toast, + messageToastify, + md, } from '../utils'; import { version } from '../version'; import { i18n } from '../i18next'; @@ -100,6 +102,22 @@ export class Navbar extends Component { {this.state.siteName} + {this.state.isLoggedIn && ( + + + + + {this.state.unreadCount > 0 && ( + + {this.state.unreadCount} + + )} + + )}
    {
    {WebSocketService.Instance.site.enable_downvotes && ( )} @@ -501,8 +501,8 @@ export class PostListing extends Component { -