mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-29 15:51:20 +00:00
Merge branch 'main' into remove_community_creator
This commit is contained in:
commit
b108938274
56 changed files with 507 additions and 300 deletions
|
@ -29,6 +29,7 @@ steps:
|
||||||
image: ekidd/rust-musl-builder:1.50.0
|
image: ekidd/rust-musl-builder:1.50.0
|
||||||
environment:
|
environment:
|
||||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||||
|
LEMMY_CONFIG_LOCATION: ../../config/config.hjson
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
RUST_TEST_THREADS: 1
|
RUST_TEST_THREADS: 1
|
||||||
commands:
|
commands:
|
||||||
|
@ -107,6 +108,7 @@ steps:
|
||||||
image: rust:1.50-slim-buster
|
image: rust:1.50-slim-buster
|
||||||
environment:
|
environment:
|
||||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||||
|
LEMMY_CONFIG_LOCATION: ../../config/config.hjson
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
RUST_TEST_THREADS: 1
|
RUST_TEST_THREADS: 1
|
||||||
commands:
|
commands:
|
||||||
|
|
99
RELEASES.md
99
RELEASES.md
|
@ -1,3 +1,102 @@
|
||||||
|
# Lemmy v0.10.3 Release (2021-04-07)
|
||||||
|
|
||||||
|
- Fixing instances page.
|
||||||
|
- Fixed unban not working.
|
||||||
|
- Fixed post title fetching and cross-post search.
|
||||||
|
- Fixed navigating to a user page.
|
||||||
|
|
||||||
|
# Lemmy v0.10.2 Release (2021-04-05)
|
||||||
|
|
||||||
|
- Forcing a crash if config.hjson fails to load. Should show errors easier.
|
||||||
|
|
||||||
|
# Lemmy v0.10.0 Release (2021-04-05)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
Since our last release in February, we've had [~150](https://github.com/LemmyNet/lemmy/compare/0.9.9...main) commits to Lemmy. The biggest changes, as we'll outline below, are a split of Lemmy's user tables into federated and local tables, necessitating a `v3` of Lemmy's API, federated moderation, i18n support in join.lemmy.ml, and lots of back-end cleanup.
|
||||||
|
|
||||||
|
### Lemmy Server
|
||||||
|
|
||||||
|
#### General
|
||||||
|
|
||||||
|
- Rewrote config implementation, finally allowing us to use newer Rust versions.
|
||||||
|
- Removed categories.
|
||||||
|
- Various refactors.
|
||||||
|
|
||||||
|
#### API
|
||||||
|
|
||||||
|
- A full list of the API changes can be seen on this diff of [lemmy-js-client: 0.9.9 -> 0.10.0](https://github.com/LemmyNet/lemmy-js-client/compare/0.9.9...0.10.0-rc.13) .
|
||||||
|
- Login invalidation on password change, thanks to @Mart-Bogdan
|
||||||
|
|
||||||
|
#### Federation
|
||||||
|
|
||||||
|
- It is now possible to add users from other instances as community mods.
|
||||||
|
- Federating Matrix ID.
|
||||||
|
- Many changes for better compatibility with ActivityPub standard.
|
||||||
|
|
||||||
|
#### Database
|
||||||
|
|
||||||
|
- Split the `user_` into `person` and `local_user` tables.
|
||||||
|
- Strictly typed commonly used ID columns, to prevent DB errors using `i32` as ids.
|
||||||
|
- Strictly typed URL fields, thanks to ajyoon.
|
||||||
|
- Created default DB forms, now used in all the unit tests.
|
||||||
|
|
||||||
|
### Lemmy UI
|
||||||
|
|
||||||
|
- Now using utf-8 emojis.
|
||||||
|
- Support for all the above changes to Lemmy.
|
||||||
|
- Typescript-safe i18n strings, thanks to @shilangyu.
|
||||||
|
- Added expandable post text (click on open book icon).
|
||||||
|
- Prettier cross-posting, which does smart quoting.
|
||||||
|
- Bugfixes for restoring scroll position on post page, custom site favicons, and autocomplete for login fields.
|
||||||
|
|
||||||
|
### Lemmy Docs
|
||||||
|
|
||||||
|
- Gazconroy built an [Async API spec for Lemmy](https://join.lemmy.ml/api/index.html), that now serves as our main API docs.
|
||||||
|
|
||||||
|
### join.lemmy.ml
|
||||||
|
|
||||||
|
- Rewrote in inferno isomorphic, added i18n support via [weblate](https://weblate.yerbamate.ml/projects/lemmy/joinlemmy/).
|
||||||
|
- Added a section on the support page thanking contributors.
|
||||||
|
- Changed some page urls / titles
|
||||||
|
|
||||||
|
## Upgrade notes
|
||||||
|
|
||||||
|
**Important**: there are multiple breaking changes:
|
||||||
|
|
||||||
|
- Configuration via environment variables is not supported anymore, you must have all your config in the [lemmy.hjson](https://github.com/LemmyNet/lemmy/blob/main/ansible/templates/config.hjson) file ( except for `LEMMY_CONFIG_LOCATION` ).
|
||||||
|
- The config format for `allowed_instances` and `blocked_instances` has changed, and you need to adjust your config file manually:
|
||||||
|
- before: `allowed_instances: ds9.lemmy.ml,enterprise.lemmy.ml`
|
||||||
|
- now: `allowed_instances: ["ds9.lemmy.ml", "enterprise.lemmy.ml"]` , and only one of the `allowed_instances` or `blocked_instances` blocks can be set.
|
||||||
|
- The API has been upgraded from `v2` to `v3`, so all clients need to be updated: [lemmy-js-client: 0.9.9 -> 0.10.0](https://github.com/LemmyNet/lemmy-js-client/compare/0.9.9...0.10.0-rc.13) .
|
||||||
|
|
||||||
|
If you'd like to make a DB backup before upgrading, follow [this guide](https://join.lemmy.ml/docs/en/administration/backup_and_restore.html).
|
||||||
|
|
||||||
|
To upgrade your instance to `v0.10.0`, simply follow the instructions in the documentation:
|
||||||
|
|
||||||
|
- [Upgrade with manual Docker installation](https://join.lemmy.ml/docs/en/administration/install_docker.html#updating)
|
||||||
|
- [Upgrade with Ansible installation](https://join.lemmy.ml/docs/en/administration/install_ansible.html)
|
||||||
|
|
||||||
|
|
||||||
|
## Compilation time
|
||||||
|
|
||||||
|
|| v0.9.0 (Rust 1.47) | v0.10.0 (Rust 1.47) | v0.10.0 (Rust 1.51) |
|
||||||
|
|-| -------- | -------- | -------- |
|
||||||
|
|Clean | 140s | 146s | 119s |
|
||||||
|
| Incremental | 28s | 22s | 19s |
|
||||||
|
|
||||||
|
Despite ongoing efforts to speed up compilation, it has actually gotten slower when comparing with the same Rust version. Only thanks to improvements in newer Rust versions has our build process gotten faster. This could be simply because we added more code, while Lemmy v0.9.0 had 22.4k lines of Rust, v0.10.0 has 23.8k (an increase of 6%).
|
||||||
|
|
||||||
|
v0.9.0 build graph:
|
||||||
|
![](https://lemmy.ml/pictrs/image/GVBqFnrLqG.jpg)
|
||||||
|
|
||||||
|
v0.10.0 build graph:
|
||||||
|
![](https://lemmy.ml/pictrs/image/NllzjVEyNK.jpg)
|
||||||
|
|
||||||
|
We extracted the crates `lemmy_api_crud` and `lemmy_apub_receive` from `lemmy_api` and `lemmy_apub`, respectively, and renamed `lemmy_structs` to `lemmy_api_common`. In the second graph you can see how parts of the api and apub crates are now built nicely in parallel, speeding up builds on multi-core systems.
|
||||||
|
|
||||||
|
On the other hand, some crates have gotten much slower to compile, in particular `lemmy_db_queries` (6.5s slower), `lemmy_apub` (6.5s slower if we include `lemmy_apub_receive`). And `lemmy_db_views` is quite slow, just as before.
|
||||||
|
|
||||||
# Lemmy v0.9.9 Release (2021-02-19)
|
# Lemmy v0.9.9 Release (2021-02-19)
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.9.9
|
0.10.3
|
||||||
|
|
|
@ -4,15 +4,22 @@
|
||||||
|
|
||||||
# settings related to the postgresql database
|
# settings related to the postgresql database
|
||||||
database: {
|
database: {
|
||||||
|
database: lemmy
|
||||||
|
user: lemmy
|
||||||
|
host: postgres
|
||||||
|
port: 5432
|
||||||
|
pool_size: 5
|
||||||
# password to connect to postgres
|
# password to connect to postgres
|
||||||
password: "{{ postgres_password }}"
|
password: "{{ postgres_password }}"
|
||||||
# host where postgres is running
|
|
||||||
host: "postgres"
|
|
||||||
}
|
}
|
||||||
# the domain name of your instance (eg "lemmy.ml")
|
# the domain name of your instance (eg "lemmy.ml")
|
||||||
hostname: "{{ domain }}"
|
hostname: "{{ domain }}"
|
||||||
|
# the port where lemmy should listen for incoming requests
|
||||||
|
port: 8536
|
||||||
# json web token for authorization between server and client
|
# json web token for authorization between server and client
|
||||||
jwt_secret: "{{ jwt_password }}"
|
jwt_secret: "{{ jwt_password }}"
|
||||||
|
# whether tls is required for activitypub. only disable this for debugging, never for producion.
|
||||||
|
tls_enabled: true
|
||||||
# email sending configuration
|
# email sending configuration
|
||||||
email: {
|
email: {
|
||||||
# hostname of the smtp server
|
# hostname of the smtp server
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
"eslint": "^7.18.0",
|
"eslint": "^7.18.0",
|
||||||
"eslint-plugin-jane": "^9.0.3",
|
"eslint-plugin-jane": "^9.0.3",
|
||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"lemmy-js-client": "0.10.0-rc.13",
|
"lemmy-js-client": "0.11.0-rc.1",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.1.2",
|
||||||
"ts-jest": "^26.4.4",
|
"ts-jest": "^26.4.4",
|
||||||
|
|
|
@ -19,7 +19,7 @@ let apShortname: string;
|
||||||
|
|
||||||
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
|
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
|
||||||
expect(userOne.person.name).toBe(userTwo.person.name);
|
expect(userOne.person.name).toBe(userTwo.person.name);
|
||||||
expect(userOne.person.preferred_username).toBe(userTwo.person.preferred_username);
|
expect(userOne.person.display_name).toBe(userTwo.person.display_name);
|
||||||
expect(userOne.person.bio).toBe(userTwo.person.bio);
|
expect(userOne.person.bio).toBe(userTwo.person.bio);
|
||||||
expect(userOne.person.actor_id).toBe(userTwo.person.actor_id);
|
expect(userOne.person.actor_id).toBe(userTwo.person.actor_id);
|
||||||
expect(userOne.person.avatar).toBe(userTwo.person.avatar);
|
expect(userOne.person.avatar).toBe(userTwo.person.avatar);
|
||||||
|
@ -49,7 +49,7 @@ test('Set some user settings, check that they are federated', async () => {
|
||||||
lang: '',
|
lang: '',
|
||||||
avatar,
|
avatar,
|
||||||
banner,
|
banner,
|
||||||
preferred_username: 'user321',
|
display_name: 'user321',
|
||||||
show_avatars: false,
|
show_avatars: false,
|
||||||
send_notifications_to_email: false,
|
send_notifications_to_email: false,
|
||||||
bio,
|
bio,
|
||||||
|
|
|
@ -3233,10 +3233,10 @@ language-tags@^1.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
language-subtag-registry "~0.3.2"
|
language-subtag-registry "~0.3.2"
|
||||||
|
|
||||||
lemmy-js-client@0.10.0-rc.13:
|
lemmy-js-client@0.11.0-rc.1:
|
||||||
version "0.10.0-rc.13"
|
version "0.11.0-rc.1"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.10.0-rc.13.tgz#ea2e88857243374d7fbd49ee6b4bb94c34359d85"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.0-rc.1.tgz#0031676be9fc787157a21dd3f5095dd1ee9e6a57"
|
||||||
integrity sha512-zodvYkwBYR7iP27ah6L/QPUphUUdq38kCH7QF2CUYBrsSAEkGmq2kdz+iusnQ1Ht7Ad80GtYycFprsZBveV5eQ==
|
integrity sha512-dtpxe/hHTbYEv2WnfGkAieOB9jyKUVED+y4DosUp/FcaatjPcMTiKOvCdMNjlvvG/9GyclWEoyNitPEsvJwjmg==
|
||||||
|
|
||||||
leven@^3.1.0:
|
leven@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
|
|
|
@ -1,3 +1,91 @@
|
||||||
{
|
{
|
||||||
hostname: "localhost:8536"
|
# # 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
|
||||||
|
user: "lemmy"
|
||||||
|
# password to connect to postgres
|
||||||
|
password: "password"
|
||||||
|
# host where postgres is running
|
||||||
|
host: "localhost"
|
||||||
|
# port where postgres can be accessed
|
||||||
|
port: 5432
|
||||||
|
# name of the postgres database for lemmy
|
||||||
|
database: "lemmy"
|
||||||
|
# maximum number of active sql connections
|
||||||
|
pool_size: 5
|
||||||
|
}
|
||||||
|
# the domain name of your instance (eg "lemmy.ml")
|
||||||
|
hostname: lemmy-alpha
|
||||||
|
# address where lemmy should listen for incoming requests
|
||||||
|
bind: "0.0.0.0"
|
||||||
|
# port where lemmy should listen for incoming requests
|
||||||
|
port: 8536
|
||||||
|
# whether tls is required for activitypub. only disable this for debugging, never for producion.
|
||||||
|
tls_enabled: true
|
||||||
|
# json web token for authorization between server and client
|
||||||
|
jwt_secret: "changeme"
|
||||||
|
# address where pictrs is available
|
||||||
|
pictrs_url: "http://pictrs:8080"
|
||||||
|
# address where iframely is available
|
||||||
|
iframely_url: "http://iframely"
|
||||||
|
# rate limits for various user actions, by user ip
|
||||||
|
rate_limit: {
|
||||||
|
# maximum number of messages created in interval
|
||||||
|
message: 180
|
||||||
|
# interval length for message limit
|
||||||
|
message_per_second: 60
|
||||||
|
# maximum number of posts created in interval
|
||||||
|
post: 6
|
||||||
|
# interval length for post limit
|
||||||
|
post_per_second: 600
|
||||||
|
# maximum number of registrations in interval
|
||||||
|
register: 3
|
||||||
|
# interval length for registration limit
|
||||||
|
register_per_second: 3600
|
||||||
|
# maximum number of image uploads in interval
|
||||||
|
image: 6
|
||||||
|
# interval length for image uploads
|
||||||
|
image_per_second: 3600
|
||||||
|
}
|
||||||
|
# settings related to activitypub federation
|
||||||
|
federation: {
|
||||||
|
# whether to enable activitypub federation.
|
||||||
|
enabled: false
|
||||||
|
# Allows and blocks are described here:
|
||||||
|
# https://join.lemmy.ml/docs/en/federation/administration.html#instance-allowlist-and-blocklist
|
||||||
|
#
|
||||||
|
# comma separated list of instances with which federation is allowed
|
||||||
|
# Only one of these blocks should be uncommented
|
||||||
|
# allowed_instances: ["instance1.tld","instance2.tld"]
|
||||||
|
# comma separated list of instances which are blocked from federating
|
||||||
|
# blocked_instances: []
|
||||||
|
}
|
||||||
|
captcha: {
|
||||||
|
enabled: true
|
||||||
|
difficulty: medium # Can be easy, medium, or hard
|
||||||
|
}
|
||||||
|
# # email sending configuration
|
||||||
|
# email: {
|
||||||
|
# # hostname and port of the smtp server
|
||||||
|
# smtp_server: ""
|
||||||
|
# # login name for smtp server
|
||||||
|
# smtp_login: ""
|
||||||
|
# # password to login to the smtp server
|
||||||
|
# smtp_password: ""
|
||||||
|
# # address to send emails from, eg "noreply@your-instance.com"
|
||||||
|
# smtp_from_address: ""
|
||||||
|
# # whether or not smtp connections should use tls
|
||||||
|
# use_tls: true
|
||||||
|
# }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
{
|
|
||||||
# # 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
|
|
||||||
user: "lemmy"
|
|
||||||
# password to connect to postgres
|
|
||||||
password: "password"
|
|
||||||
# host where postgres is running
|
|
||||||
host: "localhost"
|
|
||||||
# port where postgres can be accessed
|
|
||||||
port: 5432
|
|
||||||
# name of the postgres database for lemmy
|
|
||||||
database: "lemmy"
|
|
||||||
# maximum number of active sql connections
|
|
||||||
pool_size: 5
|
|
||||||
}
|
|
||||||
# the domain name of your instance (eg "lemmy.ml")
|
|
||||||
hostname: null
|
|
||||||
# address where lemmy should listen for incoming requests
|
|
||||||
bind: "0.0.0.0"
|
|
||||||
# port where lemmy should listen for incoming requests
|
|
||||||
port: 8536
|
|
||||||
# whether tls is required for activitypub. only disable this for debugging, never for producion.
|
|
||||||
tls_enabled: true
|
|
||||||
# json web token for authorization between server and client
|
|
||||||
jwt_secret: "changeme"
|
|
||||||
# address where pictrs is available
|
|
||||||
pictrs_url: "http://pictrs:8080"
|
|
||||||
# address where iframely is available
|
|
||||||
iframely_url: "http://iframely"
|
|
||||||
# rate limits for various user actions, by user ip
|
|
||||||
rate_limit: {
|
|
||||||
# maximum number of messages created in interval
|
|
||||||
message: 180
|
|
||||||
# interval length for message limit
|
|
||||||
message_per_second: 60
|
|
||||||
# maximum number of posts created in interval
|
|
||||||
post: 6
|
|
||||||
# interval length for post limit
|
|
||||||
post_per_second: 600
|
|
||||||
# maximum number of registrations in interval
|
|
||||||
register: 3
|
|
||||||
# interval length for registration limit
|
|
||||||
register_per_second: 3600
|
|
||||||
# maximum number of image uploads in interval
|
|
||||||
image: 6
|
|
||||||
# interval length for image uploads
|
|
||||||
image_per_second: 3600
|
|
||||||
}
|
|
||||||
# settings related to activitypub federation
|
|
||||||
federation: {
|
|
||||||
# whether to enable activitypub federation.
|
|
||||||
enabled: false
|
|
||||||
# Allows and blocks are described here:
|
|
||||||
# https://join.lemmy.ml/docs/en/federation/administration.html#instance-allowlist-and-blocklist
|
|
||||||
#
|
|
||||||
# comma separated list of instances with which federation is allowed
|
|
||||||
# Only one of these blocks should be uncommented
|
|
||||||
# allowed_instances: ["instance1.tld","instance2.tld"]
|
|
||||||
# comma separated list of instances which are blocked from federating
|
|
||||||
# blocked_instances: []
|
|
||||||
}
|
|
||||||
captcha: {
|
|
||||||
enabled: true
|
|
||||||
difficulty: medium # Can be easy, medium, or hard
|
|
||||||
}
|
|
||||||
# # email sending configuration
|
|
||||||
# email: {
|
|
||||||
# # hostname and port of the smtp server
|
|
||||||
# smtp_server: ""
|
|
||||||
# # login name for smtp server
|
|
||||||
# smtp_login: ""
|
|
||||||
# # password to login to the smtp server
|
|
||||||
# smtp_password: ""
|
|
||||||
# # address to send emails from, eg "noreply@your-instance.com"
|
|
||||||
# smtp_from_address: ""
|
|
||||||
# # whether or not smtp connections should use tls
|
|
||||||
# use_tls: true
|
|
||||||
# }
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
use actix_web::{web, web::Data};
|
use actix_web::{web, web::Data};
|
||||||
|
use captcha::Captcha;
|
||||||
use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
|
use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
|
||||||
use lemmy_utils::{ConnectionId, LemmyError};
|
use lemmy_utils::{ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
|
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{env, process::Command};
|
|
||||||
|
|
||||||
mod comment;
|
mod comment;
|
||||||
mod comment_report;
|
mod comment_report;
|
||||||
|
@ -63,6 +63,9 @@ pub async fn match_websocket_operation(
|
||||||
UserOperation::SaveUserSettings => {
|
UserOperation::SaveUserSettings => {
|
||||||
do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
|
do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
|
||||||
}
|
}
|
||||||
|
UserOperation::ChangePassword => {
|
||||||
|
do_websocket_operation::<ChangePassword>(context, id, op, data).await
|
||||||
|
}
|
||||||
UserOperation::GetReportCount => {
|
UserOperation::GetReportCount => {
|
||||||
do_websocket_operation::<GetReportCount>(context, id, op, data).await
|
do_websocket_operation::<GetReportCount>(context, id, op, data).await
|
||||||
}
|
}
|
||||||
|
@ -158,60 +161,23 @@ where
|
||||||
serialize_websocket_message(&op, &res)
|
serialize_websocket_message(&op, &res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyError> {
|
/// Converts the captcha to a base64 encoded wav audio file
|
||||||
let mut built_text = String::new();
|
pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
|
||||||
|
let letters = captcha.as_wav();
|
||||||
|
|
||||||
// Building proper speech text for espeak
|
let mut concat_letters: Vec<u8> = Vec::new();
|
||||||
for mut c in captcha.chars() {
|
|
||||||
let new_str = if c.is_alphabetic() {
|
for letter in letters {
|
||||||
if c.is_lowercase() {
|
let bytes = letter.unwrap_or_default();
|
||||||
c.make_ascii_uppercase();
|
concat_letters.extend(bytes);
|
||||||
format!("lower case {} ... ", c)
|
|
||||||
} else {
|
|
||||||
c.make_ascii_uppercase();
|
|
||||||
format!("capital {} ... ", c)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
format!("{} ...", c)
|
|
||||||
};
|
|
||||||
|
|
||||||
built_text.push_str(&new_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
espeak_wav_base64(&built_text)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
|
|
||||||
// Make a temp file path
|
|
||||||
let uuid = uuid::Uuid::new_v4().to_string();
|
|
||||||
let file_path = format!(
|
|
||||||
"{}/lemmy_espeak_{}.wav",
|
|
||||||
env::temp_dir().to_string_lossy(),
|
|
||||||
&uuid
|
|
||||||
);
|
|
||||||
|
|
||||||
// Write the wav file
|
|
||||||
Command::new("espeak")
|
|
||||||
.arg("-w")
|
|
||||||
.arg(&file_path)
|
|
||||||
.arg(text)
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
// Read the wav file bytes
|
|
||||||
let bytes = std::fs::read(&file_path)?;
|
|
||||||
|
|
||||||
// Delete the file
|
|
||||||
std::fs::remove_file(file_path)?;
|
|
||||||
|
|
||||||
// Convert to base64
|
// Convert to base64
|
||||||
let base64 = base64::encode(bytes);
|
base64::encode(concat_letters)
|
||||||
|
|
||||||
Ok(base64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::captcha_espeak_wav_base64;
|
|
||||||
use lemmy_api_common::check_validator_time;
|
use lemmy_api_common::check_validator_time;
|
||||||
use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
|
use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
|
@ -253,9 +219,4 @@ mod tests {
|
||||||
let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
|
let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
|
||||||
assert_eq!(1, num_deleted);
|
assert_eq!(1, num_deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_espeak() {
|
|
||||||
assert!(captcha_espeak_wav_base64("WxRt2l").is_ok())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{captcha_espeak_wav_base64, Perform};
|
use crate::{captcha_as_wav_base64, Perform};
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use bcrypt::verify;
|
use bcrypt::verify;
|
||||||
|
@ -58,7 +58,7 @@ use lemmy_utils::{
|
||||||
email::send_email,
|
email::send_email,
|
||||||
location_info,
|
location_info,
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
utils::{generate_random_string, is_valid_preferred_username, naive_from_unix},
|
utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix},
|
||||||
ApiError,
|
ApiError,
|
||||||
ConnectionId,
|
ConnectionId,
|
||||||
LemmyError,
|
LemmyError,
|
||||||
|
@ -133,13 +133,11 @@ impl Perform for GetCaptcha {
|
||||||
|
|
||||||
let answer = captcha.chars_as_string();
|
let answer = captcha.chars_as_string();
|
||||||
|
|
||||||
let png_byte_array = captcha.as_png().expect("failed to generate captcha");
|
let png = captcha.as_base64().expect("failed to generate captcha");
|
||||||
|
|
||||||
let png = base64::encode(png_byte_array);
|
|
||||||
|
|
||||||
let uuid = uuid::Uuid::new_v4().to_string();
|
let uuid = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
let wav = captcha_espeak_wav_base64(&answer).ok();
|
let wav = captcha_as_wav_base64(&captcha);
|
||||||
|
|
||||||
let captcha_item = CaptchaItem {
|
let captcha_item = CaptchaItem {
|
||||||
answer,
|
answer,
|
||||||
|
@ -172,7 +170,7 @@ impl Perform for SaveUserSettings {
|
||||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||||
let email = diesel_option_overwrite(&data.email);
|
let email = diesel_option_overwrite(&data.email);
|
||||||
let bio = diesel_option_overwrite(&data.bio);
|
let bio = diesel_option_overwrite(&data.bio);
|
||||||
let preferred_username = diesel_option_overwrite(&data.preferred_username);
|
let display_name = diesel_option_overwrite(&data.display_name);
|
||||||
let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
|
let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
|
||||||
|
|
||||||
if let Some(Some(bio)) = &bio {
|
if let Some(Some(bio)) = &bio {
|
||||||
|
@ -181,59 +179,30 @@ impl Perform for SaveUserSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Some(preferred_username)) = &preferred_username {
|
if let Some(Some(display_name)) = &display_name {
|
||||||
if !is_valid_preferred_username(preferred_username.trim()) {
|
if !is_valid_display_name(display_name.trim()) {
|
||||||
return Err(ApiError::err("invalid_username").into());
|
return Err(ApiError::err("invalid_username").into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(Some(matrix_user_id)) = &matrix_user_id {
|
||||||
|
if !is_valid_matrix_id(matrix_user_id) {
|
||||||
|
return Err(ApiError::err("invalid_matrix_id").into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let local_user_id = local_user_view.local_user.id;
|
let local_user_id = local_user_view.local_user.id;
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
let password_encrypted = match &data.new_password {
|
|
||||||
Some(new_password) => {
|
|
||||||
match &data.new_password_verify {
|
|
||||||
Some(new_password_verify) => {
|
|
||||||
password_length_check(&new_password)?;
|
|
||||||
|
|
||||||
// Make sure passwords match
|
|
||||||
if new_password != new_password_verify {
|
|
||||||
return Err(ApiError::err("passwords_dont_match").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the old password
|
|
||||||
match &data.old_password {
|
|
||||||
Some(old_password) => {
|
|
||||||
let valid: bool =
|
|
||||||
verify(old_password, &local_user_view.local_user.password_encrypted)
|
|
||||||
.unwrap_or(false);
|
|
||||||
if !valid {
|
|
||||||
return Err(ApiError::err("password_incorrect").into());
|
|
||||||
}
|
|
||||||
let new_password = new_password.to_owned();
|
|
||||||
let user = blocking(context.pool(), move |conn| {
|
|
||||||
LocalUser::update_password(conn, local_user_id, &new_password)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
user.password_encrypted
|
|
||||||
}
|
|
||||||
None => return Err(ApiError::err("password_incorrect").into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => return Err(ApiError::err("passwords_dont_match").into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => local_user_view.local_user.password_encrypted,
|
|
||||||
};
|
|
||||||
|
|
||||||
let default_listing_type = data.default_listing_type;
|
let default_listing_type = data.default_listing_type;
|
||||||
let default_sort_type = data.default_sort_type;
|
let default_sort_type = data.default_sort_type;
|
||||||
|
let password_encrypted = local_user_view.local_user.password_encrypted;
|
||||||
|
|
||||||
let person_form = PersonForm {
|
let person_form = PersonForm {
|
||||||
name: local_user_view.person.name,
|
name: local_user_view.person.name,
|
||||||
avatar,
|
avatar,
|
||||||
banner,
|
banner,
|
||||||
inbox_url: None,
|
inbox_url: None,
|
||||||
preferred_username,
|
display_name,
|
||||||
published: None,
|
published: None,
|
||||||
updated: Some(naive_now()),
|
updated: Some(naive_now()),
|
||||||
banned: None,
|
banned: None,
|
||||||
|
@ -265,6 +234,7 @@ impl Perform for SaveUserSettings {
|
||||||
email,
|
email,
|
||||||
password_encrypted,
|
password_encrypted,
|
||||||
show_nsfw: data.show_nsfw,
|
show_nsfw: data.show_nsfw,
|
||||||
|
show_scores: data.show_scores,
|
||||||
theme: data.theme.to_owned(),
|
theme: data.theme.to_owned(),
|
||||||
default_sort_type,
|
default_sort_type,
|
||||||
default_listing_type,
|
default_listing_type,
|
||||||
|
@ -299,6 +269,49 @@ impl Perform for SaveUserSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl Perform for ChangePassword {
|
||||||
|
type Response = LoginResponse;
|
||||||
|
|
||||||
|
async fn perform(
|
||||||
|
&self,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
_websocket_id: Option<ConnectionId>,
|
||||||
|
) -> Result<LoginResponse, LemmyError> {
|
||||||
|
let data: &ChangePassword = &self;
|
||||||
|
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
||||||
|
|
||||||
|
password_length_check(&data.new_password)?;
|
||||||
|
|
||||||
|
// Make sure passwords match
|
||||||
|
if data.new_password != data.new_password_verify {
|
||||||
|
return Err(ApiError::err("passwords_dont_match").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the old password
|
||||||
|
let valid: bool = verify(
|
||||||
|
&data.old_password,
|
||||||
|
&local_user_view.local_user.password_encrypted,
|
||||||
|
)
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !valid {
|
||||||
|
return Err(ApiError::err("password_incorrect").into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let local_user_id = local_user_view.local_user.id;
|
||||||
|
let new_password = data.new_password.to_owned();
|
||||||
|
let updated_local_user = blocking(context.pool(), move |conn| {
|
||||||
|
LocalUser::update_password(conn, local_user_id, &new_password)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
// Return the jwt
|
||||||
|
Ok(LoginResponse {
|
||||||
|
jwt: Claims::jwt(updated_local_user.id.0)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for AddAdmin {
|
impl Perform for AddAdmin {
|
||||||
type Response = AddAdminResponse;
|
type Response = AddAdminResponse;
|
||||||
|
|
|
@ -418,3 +418,12 @@ pub fn password_length_check(pass: &str) -> Result<(), LemmyError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks the site description length
|
||||||
|
pub fn site_description_length_check(description: &str) -> Result<(), LemmyError> {
|
||||||
|
if description.len() > 150 {
|
||||||
|
Err(ApiError::err("site_description_length_overflow").into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,31 +40,40 @@ pub struct GetCaptchaResponse {
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct CaptchaResponse {
|
pub struct CaptchaResponse {
|
||||||
pub png: String, // A Base64 encoded png
|
pub png: String, // A Base64 encoded png
|
||||||
pub wav: Option<String>, // A Base64 encoded wav audio
|
pub wav: String, // A Base64 encoded wav audio
|
||||||
pub uuid: String,
|
pub uuid: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct SaveUserSettings {
|
pub struct SaveUserSettings {
|
||||||
pub show_nsfw: Option<bool>,
|
pub show_nsfw: Option<bool>,
|
||||||
|
pub show_scores: Option<bool>,
|
||||||
pub theme: Option<String>,
|
pub theme: Option<String>,
|
||||||
pub default_sort_type: Option<i16>,
|
pub default_sort_type: Option<i16>,
|
||||||
pub default_listing_type: Option<i16>,
|
pub default_listing_type: Option<i16>,
|
||||||
pub lang: Option<String>,
|
pub lang: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub preferred_username: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
pub bio: Option<String>,
|
pub bio: Option<String>,
|
||||||
pub matrix_user_id: Option<String>,
|
pub matrix_user_id: Option<String>,
|
||||||
|
pub show_avatars: Option<bool>,
|
||||||
pub new_password: Option<String>,
|
pub new_password: Option<String>,
|
||||||
pub new_password_verify: Option<String>,
|
pub new_password_verify: Option<String>,
|
||||||
pub old_password: Option<String>,
|
pub old_password: Option<String>,
|
||||||
pub show_avatars: Option<bool>,
|
|
||||||
pub send_notifications_to_email: Option<bool>,
|
pub send_notifications_to_email: Option<bool>,
|
||||||
pub auth: String,
|
pub auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ChangePassword {
|
||||||
|
pub new_password: String,
|
||||||
|
pub new_password_verify: String,
|
||||||
|
pub old_password: String,
|
||||||
|
pub auth: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct LoginResponse {
|
pub struct LoginResponse {
|
||||||
pub jwt: String,
|
pub jwt: String,
|
||||||
|
|
|
@ -18,7 +18,6 @@ use lemmy_db_views_moderator::{
|
||||||
mod_sticky_post_view::ModStickyPostView,
|
mod_sticky_post_view::ModStickyPostView,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Search {
|
pub struct Search {
|
||||||
|
@ -65,9 +64,10 @@ pub struct GetModlogResponse {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct CreateSite {
|
pub struct CreateSite {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub sidebar: Option<String>,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub icon: Option<Url>,
|
pub icon: Option<String>,
|
||||||
pub banner: Option<Url>,
|
pub banner: Option<String>,
|
||||||
pub enable_downvotes: bool,
|
pub enable_downvotes: bool,
|
||||||
pub open_registration: bool,
|
pub open_registration: bool,
|
||||||
pub enable_nsfw: bool,
|
pub enable_nsfw: bool,
|
||||||
|
@ -77,6 +77,7 @@ pub struct CreateSite {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct EditSite {
|
pub struct EditSite {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub sidebar: Option<String>,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
use crate::PerformCrud;
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt, is_admin, site::*};
|
use lemmy_api_common::{
|
||||||
use lemmy_db_queries::{source::site::Site_, Crud};
|
blocking,
|
||||||
|
get_local_user_view_from_jwt,
|
||||||
|
is_admin,
|
||||||
|
site::*,
|
||||||
|
site_description_length_check,
|
||||||
|
};
|
||||||
|
use lemmy_db_queries::{
|
||||||
|
diesel_option_overwrite,
|
||||||
|
diesel_option_overwrite_to_url,
|
||||||
|
source::site::Site_,
|
||||||
|
Crud,
|
||||||
|
};
|
||||||
use lemmy_db_schema::source::site::{Site, *};
|
use lemmy_db_schema::source::site::{Site, *};
|
||||||
use lemmy_db_views::site_view::SiteView;
|
use lemmy_db_views::site_view::SiteView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -36,11 +47,21 @@ impl PerformCrud for CreateSite {
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(&local_user_view)?;
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
|
let sidebar = diesel_option_overwrite(&data.sidebar);
|
||||||
|
let description = diesel_option_overwrite(&data.description);
|
||||||
|
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||||
|
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||||
|
|
||||||
|
if let Some(Some(desc)) = &description {
|
||||||
|
site_description_length_check(desc)?;
|
||||||
|
}
|
||||||
|
|
||||||
let site_form = SiteForm {
|
let site_form = SiteForm {
|
||||||
name: data.name.to_owned(),
|
name: data.name.to_owned(),
|
||||||
description: data.description.to_owned(),
|
sidebar,
|
||||||
icon: Some(data.icon.to_owned().map(|url| url.into())),
|
description,
|
||||||
banner: Some(data.banner.to_owned().map(|url| url.into())),
|
icon,
|
||||||
|
banner,
|
||||||
creator_id: local_user_view.person.id,
|
creator_id: local_user_view.person.id,
|
||||||
enable_downvotes: data.enable_downvotes,
|
enable_downvotes: data.enable_downvotes,
|
||||||
open_registration: data.open_registration,
|
open_registration: data.open_registration,
|
||||||
|
|
|
@ -43,6 +43,7 @@ impl PerformCrud for GetSite {
|
||||||
|
|
||||||
let create_site = CreateSite {
|
let create_site = CreateSite {
|
||||||
name: setup.site_name.to_owned(),
|
name: setup.site_name.to_owned(),
|
||||||
|
sidebar: None,
|
||||||
description: None,
|
description: None,
|
||||||
icon: None,
|
icon: None,
|
||||||
banner: None,
|
banner: None,
|
||||||
|
|
|
@ -5,8 +5,14 @@ use lemmy_api_common::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_admin,
|
is_admin,
|
||||||
site::{EditSite, SiteResponse},
|
site::{EditSite, SiteResponse},
|
||||||
|
site_description_length_check,
|
||||||
|
};
|
||||||
|
use lemmy_db_queries::{
|
||||||
|
diesel_option_overwrite,
|
||||||
|
diesel_option_overwrite_to_url,
|
||||||
|
source::site::Site_,
|
||||||
|
Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_queries::{diesel_option_overwrite_to_url, source::site::Site_, Crud};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
naive_now,
|
naive_now,
|
||||||
source::site::{Site, SiteForm},
|
source::site::{Site, SiteForm},
|
||||||
|
@ -39,12 +45,19 @@ impl PerformCrud for EditSite {
|
||||||
|
|
||||||
let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
|
let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
|
||||||
|
|
||||||
|
let sidebar = diesel_option_overwrite(&data.sidebar);
|
||||||
|
let description = diesel_option_overwrite(&data.description);
|
||||||
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||||
|
|
||||||
|
if let Some(Some(desc)) = &description {
|
||||||
|
site_description_length_check(desc)?;
|
||||||
|
}
|
||||||
|
|
||||||
let site_form = SiteForm {
|
let site_form = SiteForm {
|
||||||
name: data.name.to_owned(),
|
name: data.name.to_owned(),
|
||||||
description: data.description.to_owned(),
|
sidebar,
|
||||||
|
description,
|
||||||
icon,
|
icon,
|
||||||
banner,
|
banner,
|
||||||
creator_id: found_site.creator_id,
|
creator_id: found_site.creator_id,
|
||||||
|
|
|
@ -133,6 +133,7 @@ impl PerformCrud for Register {
|
||||||
default_listing_type: Some(ListingType::Subscribed as i16),
|
default_listing_type: Some(ListingType::Subscribed as i16),
|
||||||
lang: Some("browser".into()),
|
lang: Some("browser".into()),
|
||||||
show_avatars: Some(true),
|
show_avatars: Some(true),
|
||||||
|
show_scores: Some(true),
|
||||||
send_notifications_to_email: Some(false),
|
send_notifications_to_email: Some(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,8 @@ impl ToApub for DbPerson {
|
||||||
set_content_and_source(&mut person, bio)?;
|
set_content_and_source(&mut person, bio)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(i) = self.preferred_username.to_owned() {
|
// In apub, the "name" is a display name
|
||||||
|
if let Some(i) = self.display_name.to_owned() {
|
||||||
person.set_name(i);
|
person.set_name(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +162,7 @@ impl FromApubToForm<PersonExt> for PersonForm {
|
||||||
.preferred_username()
|
.preferred_username()
|
||||||
.context(location_info!())?
|
.context(location_info!())?
|
||||||
.to_string();
|
.to_string();
|
||||||
let preferred_username: Option<String> = person
|
let display_name: Option<String> = person
|
||||||
.name()
|
.name()
|
||||||
.map(|n| n.one())
|
.map(|n| n.one())
|
||||||
.flatten()
|
.flatten()
|
||||||
|
@ -176,12 +177,12 @@ impl FromApubToForm<PersonExt> for PersonForm {
|
||||||
.map(|s| s.to_owned().into());
|
.map(|s| s.to_owned().into());
|
||||||
|
|
||||||
check_slurs(&name)?;
|
check_slurs(&name)?;
|
||||||
check_slurs_opt(&preferred_username)?;
|
check_slurs_opt(&display_name)?;
|
||||||
check_slurs_opt(&bio)?;
|
check_slurs_opt(&bio)?;
|
||||||
|
|
||||||
Ok(PersonForm {
|
Ok(PersonForm {
|
||||||
name,
|
name,
|
||||||
preferred_username: Some(preferred_username),
|
display_name: Some(display_name),
|
||||||
banned: None,
|
banned: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
avatar: avatar.map(|o| o.map(|i| i.into())),
|
avatar: avatar.map(|o| o.map(|i| i.into())),
|
||||||
|
|
|
@ -49,6 +49,7 @@ mod tests {
|
||||||
|
|
||||||
let site_form = SiteForm {
|
let site_form = SiteForm {
|
||||||
name: "test_site".into(),
|
name: "test_site".into(),
|
||||||
|
sidebar: None,
|
||||||
description: None,
|
description: None,
|
||||||
icon: None,
|
icon: None,
|
||||||
banner: None,
|
banner: None,
|
||||||
|
|
|
@ -24,6 +24,7 @@ mod safe_settings_type {
|
||||||
show_avatars,
|
show_avatars,
|
||||||
send_notifications_to_email,
|
send_notifications_to_email,
|
||||||
validator_time,
|
validator_time,
|
||||||
|
show_scores,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl ToSafeSettings for LocalUser {
|
impl ToSafeSettings for LocalUser {
|
||||||
|
@ -43,6 +44,7 @@ mod safe_settings_type {
|
||||||
show_avatars,
|
show_avatars,
|
||||||
send_notifications_to_email,
|
send_notifications_to_email,
|
||||||
validator_time,
|
validator_time,
|
||||||
|
show_scores,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod safe_type {
|
||||||
type Columns = (
|
type Columns = (
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
preferred_username,
|
display_name,
|
||||||
avatar,
|
avatar,
|
||||||
banned,
|
banned,
|
||||||
published,
|
published,
|
||||||
|
@ -37,7 +37,7 @@ mod safe_type {
|
||||||
(
|
(
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
preferred_username,
|
display_name,
|
||||||
avatar,
|
avatar,
|
||||||
banned,
|
banned,
|
||||||
published,
|
published,
|
||||||
|
@ -63,7 +63,7 @@ mod safe_type_alias_1 {
|
||||||
type Columns = (
|
type Columns = (
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
preferred_username,
|
display_name,
|
||||||
avatar,
|
avatar,
|
||||||
banned,
|
banned,
|
||||||
published,
|
published,
|
||||||
|
@ -85,7 +85,7 @@ mod safe_type_alias_1 {
|
||||||
(
|
(
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
preferred_username,
|
display_name,
|
||||||
avatar,
|
avatar,
|
||||||
banned,
|
banned,
|
||||||
published,
|
published,
|
||||||
|
@ -111,7 +111,7 @@ mod safe_type_alias_2 {
|
||||||
type Columns = (
|
type Columns = (
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
preferred_username,
|
display_name,
|
||||||
avatar,
|
avatar,
|
||||||
banned,
|
banned,
|
||||||
published,
|
published,
|
||||||
|
@ -133,7 +133,7 @@ mod safe_type_alias_2 {
|
||||||
(
|
(
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
preferred_username,
|
display_name,
|
||||||
avatar,
|
avatar,
|
||||||
banned,
|
banned,
|
||||||
published,
|
published,
|
||||||
|
@ -236,7 +236,7 @@ impl Person_ for Person {
|
||||||
|
|
||||||
diesel::update(person.find(person_id))
|
diesel::update(person.find(person_id))
|
||||||
.set((
|
.set((
|
||||||
preferred_username.eq::<Option<String>>(None),
|
display_name.eq::<Option<String>>(None),
|
||||||
bio.eq::<Option<String>>(None),
|
bio.eq::<Option<String>>(None),
|
||||||
matrix_user_id.eq::<Option<String>>(None),
|
matrix_user_id.eq::<Option<String>>(None),
|
||||||
deleted.eq(true),
|
deleted.eq(true),
|
||||||
|
@ -264,7 +264,7 @@ mod tests {
|
||||||
let expected_person = Person {
|
let expected_person = Person {
|
||||||
id: inserted_person.id,
|
id: inserted_person.id,
|
||||||
name: "holly".into(),
|
name: "holly".into(),
|
||||||
preferred_username: None,
|
display_name: None,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
banner: None,
|
banner: None,
|
||||||
banned: false,
|
banned: false,
|
||||||
|
|
|
@ -153,6 +153,7 @@ table! {
|
||||||
show_avatars -> Bool,
|
show_avatars -> Bool,
|
||||||
send_notifications_to_email -> Bool,
|
send_notifications_to_email -> Bool,
|
||||||
validator_time -> Timestamp,
|
validator_time -> Timestamp,
|
||||||
|
show_scores -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +270,7 @@ table! {
|
||||||
person (id) {
|
person (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
name -> Varchar,
|
name -> Varchar,
|
||||||
preferred_username -> Nullable<Varchar>,
|
display_name -> Nullable<Varchar>,
|
||||||
avatar -> Nullable<Varchar>,
|
avatar -> Nullable<Varchar>,
|
||||||
banned -> Bool,
|
banned -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
|
@ -420,7 +421,7 @@ table! {
|
||||||
site (id) {
|
site (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
name -> Varchar,
|
name -> Varchar,
|
||||||
description -> Nullable<Text>,
|
sidebar -> Nullable<Text>,
|
||||||
creator_id -> Int4,
|
creator_id -> Int4,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
|
@ -429,6 +430,7 @@ table! {
|
||||||
enable_nsfw -> Bool,
|
enable_nsfw -> Bool,
|
||||||
icon -> Nullable<Varchar>,
|
icon -> Nullable<Varchar>,
|
||||||
banner -> Nullable<Varchar>,
|
banner -> Nullable<Varchar>,
|
||||||
|
description -> Nullable<Text>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,7 +471,7 @@ table! {
|
||||||
person_alias_1 (id) {
|
person_alias_1 (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
name -> Varchar,
|
name -> Varchar,
|
||||||
preferred_username -> Nullable<Varchar>,
|
display_name -> Nullable<Varchar>,
|
||||||
avatar -> Nullable<Varchar>,
|
avatar -> Nullable<Varchar>,
|
||||||
banned -> Bool,
|
banned -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
|
@ -493,7 +495,7 @@ table! {
|
||||||
person_alias_2 (id) {
|
person_alias_2 (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
name -> Varchar,
|
name -> Varchar,
|
||||||
preferred_username -> Nullable<Varchar>,
|
display_name -> Nullable<Varchar>,
|
||||||
avatar -> Nullable<Varchar>,
|
avatar -> Nullable<Varchar>,
|
||||||
banned -> Bool,
|
banned -> Bool,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub struct LocalUser {
|
||||||
pub show_avatars: bool,
|
pub show_avatars: bool,
|
||||||
pub send_notifications_to_email: bool,
|
pub send_notifications_to_email: bool,
|
||||||
pub validator_time: chrono::NaiveDateTime,
|
pub validator_time: chrono::NaiveDateTime,
|
||||||
|
pub show_scores: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO redo these, check table defaults
|
// TODO redo these, check table defaults
|
||||||
|
@ -32,6 +33,7 @@ pub struct LocalUserForm {
|
||||||
pub lang: Option<String>,
|
pub lang: Option<String>,
|
||||||
pub show_avatars: Option<bool>,
|
pub show_avatars: Option<bool>,
|
||||||
pub send_notifications_to_email: Option<bool>,
|
pub send_notifications_to_email: Option<bool>,
|
||||||
|
pub show_scores: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A local user view that removes password encrypted
|
/// A local user view that removes password encrypted
|
||||||
|
@ -49,4 +51,5 @@ pub struct LocalUserSettings {
|
||||||
pub show_avatars: bool,
|
pub show_avatars: bool,
|
||||||
pub send_notifications_to_email: bool,
|
pub send_notifications_to_email: bool,
|
||||||
pub validator_time: chrono::NaiveDateTime,
|
pub validator_time: chrono::NaiveDateTime,
|
||||||
|
pub show_scores: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use serde::Serialize;
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
pub id: PersonId,
|
pub id: PersonId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub preferred_username: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub avatar: Option<DbUrl>,
|
pub avatar: Option<DbUrl>,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
|
@ -35,7 +35,7 @@ pub struct Person {
|
||||||
pub struct PersonSafe {
|
pub struct PersonSafe {
|
||||||
pub id: PersonId,
|
pub id: PersonId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub preferred_username: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub avatar: Option<DbUrl>,
|
pub avatar: Option<DbUrl>,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
|
@ -56,7 +56,7 @@ pub struct PersonSafe {
|
||||||
pub struct PersonAlias1 {
|
pub struct PersonAlias1 {
|
||||||
pub id: PersonId,
|
pub id: PersonId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub preferred_username: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub avatar: Option<DbUrl>,
|
pub avatar: Option<DbUrl>,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
|
@ -80,7 +80,7 @@ pub struct PersonAlias1 {
|
||||||
pub struct PersonSafeAlias1 {
|
pub struct PersonSafeAlias1 {
|
||||||
pub id: PersonId,
|
pub id: PersonId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub preferred_username: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub avatar: Option<DbUrl>,
|
pub avatar: Option<DbUrl>,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
|
@ -101,7 +101,7 @@ pub struct PersonSafeAlias1 {
|
||||||
pub struct PersonAlias2 {
|
pub struct PersonAlias2 {
|
||||||
pub id: PersonId,
|
pub id: PersonId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub preferred_username: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub avatar: Option<DbUrl>,
|
pub avatar: Option<DbUrl>,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
|
@ -125,7 +125,7 @@ pub struct PersonAlias2 {
|
||||||
pub struct PersonSafeAlias2 {
|
pub struct PersonSafeAlias2 {
|
||||||
pub id: PersonId,
|
pub id: PersonId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub preferred_username: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub avatar: Option<DbUrl>,
|
pub avatar: Option<DbUrl>,
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
|
@ -145,7 +145,7 @@ pub struct PersonSafeAlias2 {
|
||||||
#[table_name = "person"]
|
#[table_name = "person"]
|
||||||
pub struct PersonForm {
|
pub struct PersonForm {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub preferred_username: Option<Option<String>>,
|
pub display_name: Option<Option<String>>,
|
||||||
pub avatar: Option<Option<DbUrl>>,
|
pub avatar: Option<Option<DbUrl>>,
|
||||||
pub banned: Option<bool>,
|
pub banned: Option<bool>,
|
||||||
pub published: Option<chrono::NaiveDateTime>,
|
pub published: Option<chrono::NaiveDateTime>,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use serde::Serialize;
|
||||||
pub struct Site {
|
pub struct Site {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub sidebar: Option<String>,
|
||||||
pub creator_id: PersonId,
|
pub creator_id: PersonId,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
|
@ -15,13 +15,14 @@ pub struct Site {
|
||||||
pub enable_nsfw: bool,
|
pub enable_nsfw: bool,
|
||||||
pub icon: Option<DbUrl>,
|
pub icon: Option<DbUrl>,
|
||||||
pub banner: Option<DbUrl>,
|
pub banner: Option<DbUrl>,
|
||||||
|
pub description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset)]
|
#[derive(Insertable, AsChangeset)]
|
||||||
#[table_name = "site"]
|
#[table_name = "site"]
|
||||||
pub struct SiteForm {
|
pub struct SiteForm {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub sidebar: Option<Option<String>>,
|
||||||
pub creator_id: PersonId,
|
pub creator_id: PersonId,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub enable_downvotes: bool,
|
pub enable_downvotes: bool,
|
||||||
|
@ -30,4 +31,5 @@ pub struct SiteForm {
|
||||||
// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
|
// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
|
||||||
pub icon: Option<Option<DbUrl>>,
|
pub icon: Option<Option<DbUrl>>,
|
||||||
pub banner: Option<Option<DbUrl>>,
|
pub banner: Option<Option<DbUrl>>,
|
||||||
|
pub description: Option<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -518,7 +518,7 @@ mod tests {
|
||||||
creator: PersonSafe {
|
creator: PersonSafe {
|
||||||
id: inserted_person.id,
|
id: inserted_person.id,
|
||||||
name: "timmy".into(),
|
name: "timmy".into(),
|
||||||
preferred_username: None,
|
display_name: None,
|
||||||
published: inserted_person.published,
|
published: inserted_person.published,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
actor_id: inserted_person.actor_id.to_owned(),
|
actor_id: inserted_person.actor_id.to_owned(),
|
||||||
|
|
|
@ -540,7 +540,7 @@ mod tests {
|
||||||
creator: PersonSafe {
|
creator: PersonSafe {
|
||||||
id: inserted_person.id,
|
id: inserted_person.id,
|
||||||
name: person_name,
|
name: person_name,
|
||||||
preferred_username: None,
|
display_name: None,
|
||||||
published: inserted_person.published,
|
published: inserted_person.published,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
actor_id: inserted_person.actor_id.to_owned(),
|
actor_id: inserted_person.actor_id.to_owned(),
|
||||||
|
|
|
@ -183,6 +183,7 @@ impl<'a> CommunityQueryBuilder<'a> {
|
||||||
match self.sort {
|
match self.sort {
|
||||||
SortType::New => query = query.order_by(community::published.desc()),
|
SortType::New => query = query.order_by(community::published.desc()),
|
||||||
SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()),
|
SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()),
|
||||||
|
SortType::TopMonth => query = query.order_by(community_aggregates::users_active_month.desc()),
|
||||||
// Covers all other sorts, including hot
|
// Covers all other sorts, including hot
|
||||||
_ => {
|
_ => {
|
||||||
query = query
|
query = query
|
||||||
|
|
|
@ -21,15 +21,20 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::settings) static DEFAULT_DATABASE_USER: &str = "lemmy";
|
||||||
|
pub(in crate::settings) static DEFAULT_DATABASE_PORT: i32 = 5432;
|
||||||
|
pub(in crate::settings) static DEFAULT_DATABASE_DB: &str = "lemmy";
|
||||||
|
pub static DEFAULT_DATABASE_POOL_SIZE: u32 = 5;
|
||||||
|
|
||||||
impl Default for DatabaseConfig {
|
impl Default for DatabaseConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
user: "lemmy".into(),
|
user: Some(DEFAULT_DATABASE_USER.to_string()),
|
||||||
password: "password".into(),
|
password: "password".into(),
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 5432,
|
port: Some(DEFAULT_DATABASE_PORT),
|
||||||
database: "lemmy".into(),
|
database: Some(DEFAULT_DATABASE_DB.to_string()),
|
||||||
pool_size: 5,
|
pool_size: Some(DEFAULT_DATABASE_POOL_SIZE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
location_info,
|
location_info,
|
||||||
settings::structs::{
|
settings::{
|
||||||
|
defaults::{DEFAULT_DATABASE_DB, DEFAULT_DATABASE_PORT, DEFAULT_DATABASE_USER},
|
||||||
|
structs::{
|
||||||
CaptchaConfig,
|
CaptchaConfig,
|
||||||
DatabaseConfig,
|
DatabaseConfig,
|
||||||
EmailConfig,
|
EmailConfig,
|
||||||
|
@ -9,30 +11,22 @@ use crate::{
|
||||||
Settings,
|
Settings,
|
||||||
SetupConfig,
|
SetupConfig,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use deser_hjson::from_str;
|
use deser_hjson::from_str;
|
||||||
use log::warn;
|
|
||||||
use merge::Merge;
|
use merge::Merge;
|
||||||
use std::{env, fs, io::Error, net::IpAddr, sync::RwLock};
|
use std::{env, fs, io::Error, net::IpAddr, sync::RwLock};
|
||||||
|
|
||||||
pub(crate) mod defaults;
|
pub mod defaults;
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
|
|
||||||
static CONFIG_FILE: &str = "config/config.hjson";
|
static CONFIG_FILE: &str = "config/config.hjson";
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SETTINGS: RwLock<Settings> = RwLock::new(match Settings::init() {
|
static ref SETTINGS: RwLock<Settings> =
|
||||||
Ok(c) => c,
|
RwLock::new(Settings::init().expect("Failed to load settings file"));
|
||||||
Err(e) => {
|
|
||||||
warn!(
|
|
||||||
"Couldn't load settings file, using default settings.\n{}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
Settings::default()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
@ -69,7 +63,15 @@ impl Settings {
|
||||||
let conf = self.database();
|
let conf = self.database();
|
||||||
format!(
|
format!(
|
||||||
"postgres://{}:{}@{}:{}/{}",
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
conf.user, conf.password, conf.host, conf.port, conf.database,
|
conf
|
||||||
|
.user
|
||||||
|
.unwrap_or_else(|| DEFAULT_DATABASE_USER.to_string()),
|
||||||
|
conf.password,
|
||||||
|
conf.host,
|
||||||
|
conf.port.unwrap_or(DEFAULT_DATABASE_PORT),
|
||||||
|
conf
|
||||||
|
.database
|
||||||
|
.unwrap_or_else(|| DEFAULT_DATABASE_DB.to_string()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,12 @@ pub struct CaptchaConfig {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct DatabaseConfig {
|
pub struct DatabaseConfig {
|
||||||
pub user: String,
|
pub user: Option<String>,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: i32,
|
pub port: Option<i32>,
|
||||||
pub database: String,
|
pub database: Option<String>,
|
||||||
pub pool_size: u32,
|
pub pool_size: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
is_valid_community_name,
|
is_valid_community_name,
|
||||||
|
is_valid_display_name,
|
||||||
|
is_valid_matrix_id,
|
||||||
is_valid_post_title,
|
is_valid_post_title,
|
||||||
is_valid_preferred_username,
|
|
||||||
is_valid_username,
|
is_valid_username,
|
||||||
remove_slurs,
|
remove_slurs,
|
||||||
scrape_text_for_mentions,
|
scrape_text_for_mentions,
|
||||||
|
@ -29,9 +30,15 @@ fn test_valid_register_username() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_preferred_username() {
|
fn test_valid_display_name() {
|
||||||
assert!(is_valid_preferred_username("hello @there"));
|
assert!(is_valid_display_name("hello @there"));
|
||||||
assert!(!is_valid_preferred_username("@hello there"));
|
assert!(!is_valid_display_name("@hello there"));
|
||||||
|
|
||||||
|
// Make sure zero-space with an @ doesn't work
|
||||||
|
assert!(!is_valid_display_name(&format!(
|
||||||
|
"{}@my name is",
|
||||||
|
'\u{200b}'
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -50,6 +57,14 @@ fn test_valid_post_title() {
|
||||||
assert!(!is_valid_post_title("\n \n \n \n ")); // tabs/spaces/newlines
|
assert!(!is_valid_post_title("\n \n \n \n ")); // tabs/spaces/newlines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_valid_matrix_id() {
|
||||||
|
assert!(is_valid_matrix_id("@dess:matrix.org"));
|
||||||
|
assert!(!is_valid_matrix_id("dess:matrix.org"));
|
||||||
|
assert!(!is_valid_matrix_id(" @dess:matrix.org"));
|
||||||
|
assert!(!is_valid_matrix_id("@dess:matrix.org t"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slur_filter() {
|
fn test_slur_filter() {
|
||||||
let test =
|
let test =
|
||||||
|
|
|
@ -15,6 +15,7 @@ lazy_static! {
|
||||||
static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").expect("compile regex");
|
static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").expect("compile regex");
|
||||||
static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").expect("compile regex");
|
static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").expect("compile regex");
|
||||||
static ref VALID_POST_TITLE_REGEX: Regex = Regex::new(r".*\S.*").expect("compile regex");
|
static ref VALID_POST_TITLE_REGEX: Regex = Regex::new(r".*\S.*").expect("compile regex");
|
||||||
|
static ref VALID_MATRIX_ID_REGEX: Regex = Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn naive_from_unix(time: i64) -> NaiveDateTime {
|
pub fn naive_from_unix(time: i64) -> NaiveDateTime {
|
||||||
|
@ -108,10 +109,15 @@ pub fn is_valid_username(name: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't do a regex here, reverse lookarounds not supported
|
// Can't do a regex here, reverse lookarounds not supported
|
||||||
pub fn is_valid_preferred_username(preferred_username: &str) -> bool {
|
pub fn is_valid_display_name(name: &str) -> bool {
|
||||||
!preferred_username.starts_with('@')
|
!name.starts_with('@')
|
||||||
&& preferred_username.chars().count() >= 3
|
&& !name.starts_with('\u{200b}')
|
||||||
&& preferred_username.chars().count() <= 20
|
&& name.chars().count() >= 3
|
||||||
|
&& name.chars().count() <= 20
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
|
||||||
|
VALID_MATRIX_ID_REGEX.is_match(matrix_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_community_name(name: &str) -> bool {
|
pub fn is_valid_community_name(name: &str) -> bool {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
pub const VERSION: &str = "0.10.0-rc.13";
|
pub const VERSION: &str = "0.10.3";
|
||||||
|
|
|
@ -123,6 +123,7 @@ pub enum UserOperation {
|
||||||
PostJoin,
|
PostJoin,
|
||||||
CommunityJoin,
|
CommunityJoin,
|
||||||
ModJoin,
|
ModJoin,
|
||||||
|
ChangePassword,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumString, ToString, Debug, Clone)]
|
#[derive(EnumString, ToString, Debug, Clone)]
|
||||||
|
|
|
@ -49,9 +49,6 @@ FROM alpine:3.12 as lemmy
|
||||||
# Install libpq for postgres
|
# Install libpq for postgres
|
||||||
RUN apk add libpq
|
RUN apk add libpq
|
||||||
|
|
||||||
# Install Espeak for captchas
|
|
||||||
RUN apk add espeak
|
|
||||||
|
|
||||||
RUN addgroup -g 1000 lemmy
|
RUN addgroup -g 1000 lemmy
|
||||||
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
|
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ services:
|
||||||
- iframely
|
- iframely
|
||||||
|
|
||||||
lemmy-ui:
|
lemmy-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.13
|
image: dessalines/lemmy-ui:0.10.3
|
||||||
ports:
|
ports:
|
||||||
- "1235:1234"
|
- "1235:1234"
|
||||||
restart: always
|
restart: always
|
||||||
|
|
|
@ -8,4 +8,4 @@ set -e
|
||||||
mkdir -p volumes/pictrs
|
mkdir -p volumes/pictrs
|
||||||
sudo chown -R 991:991 volumes/pictrs
|
sudo chown -R 991:991 volumes/pictrs
|
||||||
sudo docker build ../../ --file ../dev/volume_mount.dockerfile -t lemmy-dev:latest
|
sudo docker build ../../ --file ../dev/volume_mount.dockerfile -t lemmy-dev:latest
|
||||||
sudo docker-compose up -d
|
sudo docker-compose up
|
||||||
|
|
|
@ -19,12 +19,11 @@ RUN --mount=type=cache,target=/app/target \
|
||||||
|
|
||||||
FROM ubuntu:20.10
|
FROM ubuntu:20.10
|
||||||
|
|
||||||
# Install libpq for postgres and espeak
|
# Install libpq for postgres
|
||||||
RUN apt-get update -y
|
RUN apt-get update -y
|
||||||
RUN apt-get install -y libpq-dev espeak
|
RUN apt-get install -y libpq-dev
|
||||||
|
|
||||||
# Copy resources
|
# Copy resources
|
||||||
COPY config/defaults.hjson /config/defaults.hjson
|
|
||||||
COPY --from=rust /app/lemmy_server /app/lemmy
|
COPY --from=rust /app/lemmy_server /app/lemmy
|
||||||
|
|
||||||
EXPOSE 8536
|
EXPOSE 8536
|
||||||
|
|
|
@ -29,7 +29,7 @@ services:
|
||||||
- ./volumes/pictrs_alpha:/mnt
|
- ./volumes/pictrs_alpha:/mnt
|
||||||
|
|
||||||
lemmy-alpha-ui:
|
lemmy-alpha-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.13
|
image: dessalines/lemmy-ui:0.10.3
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-alpha:8541
|
- LEMMY_INTERNAL_HOST=lemmy-alpha:8541
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8541
|
- LEMMY_EXTERNAL_HOST=localhost:8541
|
||||||
|
@ -58,7 +58,7 @@ services:
|
||||||
- ./volumes/postgres_alpha:/var/lib/postgresql/data
|
- ./volumes/postgres_alpha:/var/lib/postgresql/data
|
||||||
|
|
||||||
lemmy-beta-ui:
|
lemmy-beta-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.13
|
image: dessalines/lemmy-ui:0.10.3
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-beta:8551
|
- LEMMY_INTERNAL_HOST=lemmy-beta:8551
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8551
|
- LEMMY_EXTERNAL_HOST=localhost:8551
|
||||||
|
@ -87,7 +87,7 @@ services:
|
||||||
- ./volumes/postgres_beta:/var/lib/postgresql/data
|
- ./volumes/postgres_beta:/var/lib/postgresql/data
|
||||||
|
|
||||||
lemmy-gamma-ui:
|
lemmy-gamma-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.13
|
image: dessalines/lemmy-ui:0.10.3
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-gamma:8561
|
- LEMMY_INTERNAL_HOST=lemmy-gamma:8561
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8561
|
- LEMMY_EXTERNAL_HOST=localhost:8561
|
||||||
|
@ -117,7 +117,7 @@ services:
|
||||||
|
|
||||||
# An instance with only an allowlist for beta
|
# An instance with only an allowlist for beta
|
||||||
lemmy-delta-ui:
|
lemmy-delta-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.13
|
image: dessalines/lemmy-ui:0.10.3
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-delta:8571
|
- LEMMY_INTERNAL_HOST=lemmy-delta:8571
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8571
|
- LEMMY_EXTERNAL_HOST=localhost:8571
|
||||||
|
@ -147,7 +147,7 @@ services:
|
||||||
|
|
||||||
# An instance who has a blocklist, with lemmy-alpha blocked
|
# An instance who has a blocklist, with lemmy-alpha blocked
|
||||||
lemmy-epsilon-ui:
|
lemmy-epsilon-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.13
|
image: dessalines/lemmy-ui:0.10.3
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-epsilon:8581
|
- LEMMY_INTERNAL_HOST=lemmy-epsilon:8581
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8581
|
- LEMMY_EXTERNAL_HOST=localhost:8581
|
||||||
|
|
|
@ -49,9 +49,6 @@ FROM alpine:3.12 as lemmy
|
||||||
# Install libpq for postgres
|
# Install libpq for postgres
|
||||||
RUN apk add libpq
|
RUN apk add libpq
|
||||||
|
|
||||||
# Install Espeak for captchas
|
|
||||||
RUN apk add espeak
|
|
||||||
|
|
||||||
RUN addgroup -g 1000 lemmy
|
RUN addgroup -g 1000 lemmy
|
||||||
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
|
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@ RUN cp ./target/release/lemmy_server /app/lemmy_server
|
||||||
# The Debian runner
|
# The Debian runner
|
||||||
FROM debian:buster-slim as lemmy
|
FROM debian:buster-slim as lemmy
|
||||||
|
|
||||||
# Install libpq for postgres and espeak for captchas
|
# Install libpq for postgres
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install --no-install-recommends espeak postgresql-client libc6 libssl1.1 \
|
&& apt-get -y install --no-install-recommends postgresql-client libc6 libssl1.1 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN addgroup --gid 1000 lemmy
|
RUN addgroup --gid 1000 lemmy
|
||||||
|
|
|
@ -13,7 +13,9 @@ git add "crates/utils/src/version.rs"
|
||||||
popd
|
popd
|
||||||
|
|
||||||
# Changing various references to the Lemmy version
|
# Changing various references to the Lemmy version
|
||||||
|
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../dev/docker-compose.yml
|
||||||
sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../dev/docker-compose.yml
|
sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../dev/docker-compose.yml
|
||||||
|
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../federation/docker-compose.yml
|
||||||
sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../federation/docker-compose.yml
|
sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../federation/docker-compose.yml
|
||||||
git add ../dev/docker-compose.yml
|
git add ../dev/docker-compose.yml
|
||||||
git add ../federation/docker-compose.yml
|
git add ../federation/docker-compose.yml
|
||||||
|
@ -22,6 +24,7 @@ git add ../federation/docker-compose.yml
|
||||||
# IE, when the third semver is a number, not '2-rc'
|
# IE, when the third semver is a number, not '2-rc'
|
||||||
if [ ! -z "${third_semver##*[!0-9]*}" ]; then
|
if [ ! -z "${third_semver##*[!0-9]*}" ]; then
|
||||||
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../prod/docker-compose.yml
|
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../prod/docker-compose.yml
|
||||||
|
sed -i "s/dessalines\/lemmy-ui:.*/dessalines\/lemmy-ui:$new_tag/" ../prod/docker-compose.yml
|
||||||
git add ../prod/docker-compose.yml
|
git add ../prod/docker-compose.yml
|
||||||
|
|
||||||
# Setting the version for Ansible
|
# Setting the version for Ansible
|
||||||
|
|
|
@ -12,7 +12,7 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
lemmy:
|
lemmy:
|
||||||
image: dessalines/lemmy:0.9.9
|
image: dessalines/lemmy:0.10.3
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8536:8536"
|
- "127.0.0.1:8536:8536"
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -26,7 +26,7 @@ services:
|
||||||
- iframely
|
- iframely
|
||||||
|
|
||||||
lemmy-ui:
|
lemmy-ui:
|
||||||
image: dessalines/lemmy-ui:0.9.9
|
image: dessalines/lemmy-ui:0.10.3
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:1235:1234"
|
- "127.0.0.1:1235:1234"
|
||||||
restart: always
|
restart: always
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
alter table local_user drop column show_scores;
|
|
@ -0,0 +1 @@
|
||||||
|
alter table local_user add column show_scores boolean default true not null;
|
|
@ -0,0 +1,2 @@
|
||||||
|
alter table site drop column description;
|
||||||
|
alter table site rename column sidebar to description;
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- Renaming description to sidebar
|
||||||
|
alter table site rename column description to sidebar;
|
||||||
|
|
||||||
|
-- Adding a short description column
|
||||||
|
alter table site add column description varchar(150);
|
|
@ -0,0 +1,6 @@
|
||||||
|
alter table person rename display_name to preferred_username;
|
||||||
|
|
||||||
|
-- Regenerate the person_alias views
|
||||||
|
drop view person_alias_1, person_alias_2;
|
||||||
|
create view person_alias_1 as select * from person;
|
||||||
|
create view person_alias_2 as select * from person;
|
|
@ -0,0 +1,6 @@
|
||||||
|
alter table person rename preferred_username to display_name;
|
||||||
|
|
||||||
|
-- Regenerate the person_alias views
|
||||||
|
drop view person_alias_1, person_alias_2;
|
||||||
|
create view person_alias_1 as select * from person;
|
||||||
|
create view person_alias_2 as select * from person;
|
|
@ -0,0 +1 @@
|
||||||
|
drop index idx_community_aggregates_users_active_month;
|
|
@ -0,0 +1,2 @@
|
||||||
|
create index idx_community_aggregates_users_active_month on community_aggregates (users_active_month desc);
|
||||||
|
|
|
@ -5,5 +5,8 @@ psql -U lemmy -d postgres -c "DROP DATABASE lemmy;"
|
||||||
psql -U lemmy -d postgres -c "CREATE DATABASE lemmy;"
|
psql -U lemmy -d postgres -c "CREATE DATABASE lemmy;"
|
||||||
|
|
||||||
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
|
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
|
||||||
|
# tests are executed in working directory crates/api (or similar),
|
||||||
|
# so to load the config we need to traverse to the repo root
|
||||||
|
export LEMMY_CONFIG_LOCATION=../../config/config.hjson
|
||||||
RUST_BACKTRACE=1 \
|
RUST_BACKTRACE=1 \
|
||||||
cargo test --workspace --no-fail-fast
|
cargo test --workspace --no-fail-fast
|
||||||
|
|
|
@ -182,6 +182,10 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
||||||
"/save_user_settings",
|
"/save_user_settings",
|
||||||
web::put().to(route_post::<SaveUserSettings>),
|
web::put().to(route_post::<SaveUserSettings>),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/change_password",
|
||||||
|
web::put().to(route_post::<ChangePassword>),
|
||||||
|
)
|
||||||
.route("/report_count", web::get().to(route_get::<GetReportCount>)),
|
.route("/report_count", web::get().to(route_get::<GetReportCount>)),
|
||||||
)
|
)
|
||||||
// Admin Actions
|
// Admin Actions
|
||||||
|
|
|
@ -16,7 +16,7 @@ use lemmy_routes::{feeds, images, nodeinfo, webfinger};
|
||||||
use lemmy_server::{api_routes, code_migrations::run_advanced_migrations, scheduled_tasks};
|
use lemmy_server::{api_routes, code_migrations::run_advanced_migrations, scheduled_tasks};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
rate_limit::{rate_limiter::RateLimiter, RateLimit},
|
rate_limit::{rate_limiter::RateLimiter, RateLimit},
|
||||||
settings::structs::Settings,
|
settings::{defaults::DEFAULT_DATABASE_POOL_SIZE, structs::Settings},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
||||||
|
@ -38,7 +38,12 @@ async fn main() -> Result<(), LemmyError> {
|
||||||
};
|
};
|
||||||
let manager = ConnectionManager::<PgConnection>::new(&db_url);
|
let manager = ConnectionManager::<PgConnection>::new(&db_url);
|
||||||
let pool = Pool::builder()
|
let pool = Pool::builder()
|
||||||
.max_size(settings.database().pool_size)
|
.max_size(
|
||||||
|
settings
|
||||||
|
.database()
|
||||||
|
.pool_size
|
||||||
|
.unwrap_or(DEFAULT_DATABASE_POOL_SIZE),
|
||||||
|
)
|
||||||
.build(manager)
|
.build(manager)
|
||||||
.unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
|
.unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue