From d04e5933d429f4f3f7c0a70bf87abef409026298 Mon Sep 17 00:00:00 2001 From: Mikoto <60188643+avdb13@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:14:40 +0000 Subject: [PATCH] initial commit (#62) --- .gitignore | 18 - CONTRIBUTING.md | 114 --- Cargo.lock | 691 ++++++++++++++++++ Cargo.toml | 111 +-- Dockerfile | 7 - Justfile | 88 --- commune-example.toml | 23 - crates/core/Cargo.toml | 32 - crates/core/src/account.rs | 8 - crates/core/src/account/email.rs | 32 - crates/core/src/account/login.rs | 21 - crates/core/src/account/logout.rs | 12 - crates/core/src/account/password.rs | 20 - crates/core/src/account/register.rs | 53 -- crates/core/src/account/token.rs | 12 - crates/core/src/account/username.rs | 12 - crates/core/src/account/whoami.rs | 12 - crates/core/src/config.rs | 34 - crates/core/src/error.rs | 32 - crates/core/src/lib.rs | 128 ---- crates/core/src/profile.rs | 2 - crates/core/src/profile/avatar.rs | 41 -- crates/core/src/profile/display_name.rs | 38 - crates/core/src/util.rs | 1 - crates/core/src/util/secret.rs | 76 -- crates/matrix/Cargo.toml | 35 - crates/matrix/src/admin.rs | 8 - .../matrix/src/admin/registration_tokens.rs | 1 - .../src/admin/registration_tokens/new.rs | 38 - crates/matrix/src/admin/room.rs | 59 -- crates/matrix/src/admin/room/delete_room.rs | 47 -- .../admin/room/forward_extremities/delete.rs | 0 .../src/admin/room/forward_extremities/get.rs | 0 crates/matrix/src/admin/room/get_members.rs | 27 - crates/matrix/src/admin/room/get_room.rs | 27 - crates/matrix/src/admin/room/get_rooms.rs | 83 --- crates/matrix/src/admin/room/get_state.rs | 36 - crates/matrix/src/admin/session.rs | 46 -- crates/matrix/src/admin/session/get_nonce.rs | 22 - crates/matrix/src/admin/session/register.rs | 43 -- crates/matrix/src/admin/user.rs | 53 -- crates/matrix/src/admin/user/get_user.rs | 28 - .../matrix/src/admin/user/get_user_by_3pid.rs | 32 - crates/matrix/src/admin/user/get_users.rs | 84 --- crates/matrix/src/admin/user/set_user.rs | 28 - .../matrix/src/client-backup/account.rs.bk.bk | 3 - .../src/client-backup/events.rs.bk.bk.bk | 310 -------- .../src/client-backup/membership.rs.bk.bk | 5 - crates/matrix/src/client-backup/mod.rs.bk.bk | 10 - .../matrix/src/client-backup/mxc.rs.bk.bk.bk | 184 ----- .../matrix/src/client-backup/rooms.rs.bk.bk | 6 - .../src/client-backup/session.rs.bk.bk.bk | 2 - crates/matrix/src/client-backup/sync.rs.bk.bk | 564 -------------- crates/matrix/src/client-backup/uiaa.rs.bk.bk | 164 ----- crates/matrix/src/client.rs | 10 - crates/matrix/src/client/account.rs | 2 - crates/matrix/src/client/account/password.rs | 55 -- crates/matrix/src/client/account/whoami.rs | 32 - crates/matrix/src/client/login.rs | 134 ---- crates/matrix/src/client/logout.rs | 2 - crates/matrix/src/client/logout/all.rs | 1 - crates/matrix/src/client/logout/root.rs | 29 - crates/matrix/src/client/profile.rs | 2 - .../matrix/src/client/profile/avatar_url.rs | 2 - .../src/client/profile/avatar_url/get.rs | 31 - .../src/client/profile/avatar_url/update.rs | 36 - .../matrix/src/client/profile/display_name.rs | 2 - .../src/client/profile/display_name/get.rs | 32 - .../src/client/profile/display_name/update.rs | 35 - crates/matrix/src/client/register.rs | 3 - .../matrix/src/client/register/available.rs | 33 - crates/matrix/src/client/register/root.rs | 76 -- crates/matrix/src/client/register/token.rs | 1 - .../src/client/register/token/validity.rs | 31 - crates/matrix/src/client/uiaa.rs | 164 ----- crates/matrix/src/lib.rs | 58 -- crates/router/Cargo.toml | 32 - crates/router/src/api.rs | 7 - crates/router/src/api/account.rs | 5 - crates/router/src/api/account/avatar.rs | 31 - crates/router/src/api/account/display_name.rs | 30 - crates/router/src/api/account/email.rs | 19 - crates/router/src/api/account/password.rs | 40 - crates/router/src/api/account/whoami.rs | 21 - crates/router/src/api/relative.rs | 4 - crates/router/src/api/relative/available.rs | 18 - crates/router/src/api/relative/login.rs | 25 - crates/router/src/api/relative/logout.rs | 21 - crates/router/src/api/relative/register.rs | 25 - crates/router/src/lib.rs | 48 -- crates/router/src/main.rs | 13 - crates/router/src/router/api/mod.rs | 90 --- .../router/src/router/api/v1/account/email.rs | 34 - .../router/src/router/api/v1/account/login.rs | 95 --- .../router/src/router/api/v1/account/mod.rs | 35 - .../router/src/router/api/v1/account/root.rs | 128 ---- .../src/router/api/v1/account/session.rs | 48 -- .../src/router/api/v1/account/verify_code.rs | 74 -- .../api/v1/account/verify_code_email.rs | 76 -- crates/router/src/router/api/v1/mod.rs | 11 - crates/test/Cargo.toml | 27 - crates/test/fixtures/synapse/homeserver.yaml | 84 --- .../synapse/matrix.localhost.log.config | 35 - .../synapse/matrix.localhost.signing.key | 1 - crates/test/src/api.rs | 7 - crates/test/src/api/account.rs | 5 - crates/test/src/api/account/avatar.rs | 33 - crates/test/src/api/account/display_name.rs | 27 - crates/test/src/api/account/email.rs | 19 - crates/test/src/api/account/password.rs | 37 - crates/test/src/api/account/whoami.rs | 21 - crates/test/src/api/relative.rs | 4 - crates/test/src/api/relative/available.rs | 1 - crates/test/src/api/relative/login.rs | 34 - crates/test/src/api/relative/logout.rs | 30 - crates/test/src/api/relative/register.rs | 42 -- crates/test/src/env.rs | 66 -- crates/test/src/lib.rs | 17 - docker-compose.yml | 45 -- docs/diagrams/diagram.excalidraw | 475 ------------ docs/diagrams/diagram.png | Bin 27142 -> 0 bytes fixtures/generate_mac.py | 32 - src/api.rs | 3 + src/api/ping.rs | 20 + src/main.rs | 7 + 125 files changed, 758 insertions(+), 5578 deletions(-) delete mode 100644 CONTRIBUTING.md create mode 100644 Cargo.lock delete mode 100644 Dockerfile delete mode 100644 Justfile delete mode 100644 commune-example.toml delete mode 100644 crates/core/Cargo.toml delete mode 100644 crates/core/src/account.rs delete mode 100644 crates/core/src/account/email.rs delete mode 100644 crates/core/src/account/login.rs delete mode 100644 crates/core/src/account/logout.rs delete mode 100644 crates/core/src/account/password.rs delete mode 100644 crates/core/src/account/register.rs delete mode 100644 crates/core/src/account/token.rs delete mode 100644 crates/core/src/account/username.rs delete mode 100644 crates/core/src/account/whoami.rs delete mode 100644 crates/core/src/config.rs delete mode 100644 crates/core/src/error.rs delete mode 100644 crates/core/src/lib.rs delete mode 100644 crates/core/src/profile.rs delete mode 100644 crates/core/src/profile/avatar.rs delete mode 100644 crates/core/src/profile/display_name.rs delete mode 100644 crates/core/src/util.rs delete mode 100644 crates/core/src/util/secret.rs delete mode 100644 crates/matrix/Cargo.toml delete mode 100644 crates/matrix/src/admin.rs delete mode 100644 crates/matrix/src/admin/registration_tokens.rs delete mode 100644 crates/matrix/src/admin/registration_tokens/new.rs delete mode 100644 crates/matrix/src/admin/room.rs delete mode 100644 crates/matrix/src/admin/room/delete_room.rs delete mode 100644 crates/matrix/src/admin/room/forward_extremities/delete.rs delete mode 100644 crates/matrix/src/admin/room/forward_extremities/get.rs delete mode 100644 crates/matrix/src/admin/room/get_members.rs delete mode 100644 crates/matrix/src/admin/room/get_room.rs delete mode 100644 crates/matrix/src/admin/room/get_rooms.rs delete mode 100644 crates/matrix/src/admin/room/get_state.rs delete mode 100644 crates/matrix/src/admin/session.rs delete mode 100644 crates/matrix/src/admin/session/get_nonce.rs delete mode 100644 crates/matrix/src/admin/session/register.rs delete mode 100644 crates/matrix/src/admin/user.rs delete mode 100644 crates/matrix/src/admin/user/get_user.rs delete mode 100644 crates/matrix/src/admin/user/get_user_by_3pid.rs delete mode 100644 crates/matrix/src/admin/user/get_users.rs delete mode 100644 crates/matrix/src/admin/user/set_user.rs delete mode 100644 crates/matrix/src/client-backup/account.rs.bk.bk delete mode 100644 crates/matrix/src/client-backup/events.rs.bk.bk.bk delete mode 100644 crates/matrix/src/client-backup/membership.rs.bk.bk delete mode 100644 crates/matrix/src/client-backup/mod.rs.bk.bk delete mode 100644 crates/matrix/src/client-backup/mxc.rs.bk.bk.bk delete mode 100644 crates/matrix/src/client-backup/rooms.rs.bk.bk delete mode 100644 crates/matrix/src/client-backup/session.rs.bk.bk.bk delete mode 100644 crates/matrix/src/client-backup/sync.rs.bk.bk delete mode 100644 crates/matrix/src/client-backup/uiaa.rs.bk.bk delete mode 100644 crates/matrix/src/client.rs delete mode 100644 crates/matrix/src/client/account.rs delete mode 100644 crates/matrix/src/client/account/password.rs delete mode 100644 crates/matrix/src/client/account/whoami.rs delete mode 100644 crates/matrix/src/client/login.rs delete mode 100644 crates/matrix/src/client/logout.rs delete mode 100644 crates/matrix/src/client/logout/all.rs delete mode 100644 crates/matrix/src/client/logout/root.rs delete mode 100644 crates/matrix/src/client/profile.rs delete mode 100644 crates/matrix/src/client/profile/avatar_url.rs delete mode 100644 crates/matrix/src/client/profile/avatar_url/get.rs delete mode 100644 crates/matrix/src/client/profile/avatar_url/update.rs delete mode 100644 crates/matrix/src/client/profile/display_name.rs delete mode 100644 crates/matrix/src/client/profile/display_name/get.rs delete mode 100644 crates/matrix/src/client/profile/display_name/update.rs delete mode 100644 crates/matrix/src/client/register.rs delete mode 100644 crates/matrix/src/client/register/available.rs delete mode 100644 crates/matrix/src/client/register/root.rs delete mode 100644 crates/matrix/src/client/register/token.rs delete mode 100644 crates/matrix/src/client/register/token/validity.rs delete mode 100644 crates/matrix/src/client/uiaa.rs delete mode 100644 crates/matrix/src/lib.rs delete mode 100644 crates/router/Cargo.toml delete mode 100644 crates/router/src/api.rs delete mode 100644 crates/router/src/api/account.rs delete mode 100644 crates/router/src/api/account/avatar.rs delete mode 100644 crates/router/src/api/account/display_name.rs delete mode 100644 crates/router/src/api/account/email.rs delete mode 100644 crates/router/src/api/account/password.rs delete mode 100644 crates/router/src/api/account/whoami.rs delete mode 100644 crates/router/src/api/relative.rs delete mode 100644 crates/router/src/api/relative/available.rs delete mode 100644 crates/router/src/api/relative/login.rs delete mode 100644 crates/router/src/api/relative/logout.rs delete mode 100644 crates/router/src/api/relative/register.rs delete mode 100644 crates/router/src/lib.rs delete mode 100644 crates/router/src/main.rs delete mode 100644 crates/router/src/router/api/mod.rs delete mode 100644 crates/router/src/router/api/v1/account/email.rs delete mode 100644 crates/router/src/router/api/v1/account/login.rs delete mode 100644 crates/router/src/router/api/v1/account/mod.rs delete mode 100644 crates/router/src/router/api/v1/account/root.rs delete mode 100644 crates/router/src/router/api/v1/account/session.rs delete mode 100644 crates/router/src/router/api/v1/account/verify_code.rs delete mode 100644 crates/router/src/router/api/v1/account/verify_code_email.rs delete mode 100644 crates/router/src/router/api/v1/mod.rs delete mode 100644 crates/test/Cargo.toml delete mode 100644 crates/test/fixtures/synapse/homeserver.yaml delete mode 100644 crates/test/fixtures/synapse/matrix.localhost.log.config delete mode 100644 crates/test/fixtures/synapse/matrix.localhost.signing.key delete mode 100644 crates/test/src/api.rs delete mode 100644 crates/test/src/api/account.rs delete mode 100644 crates/test/src/api/account/avatar.rs delete mode 100644 crates/test/src/api/account/display_name.rs delete mode 100644 crates/test/src/api/account/email.rs delete mode 100644 crates/test/src/api/account/password.rs delete mode 100644 crates/test/src/api/account/whoami.rs delete mode 100644 crates/test/src/api/relative.rs delete mode 100644 crates/test/src/api/relative/available.rs delete mode 100644 crates/test/src/api/relative/login.rs delete mode 100644 crates/test/src/api/relative/logout.rs delete mode 100644 crates/test/src/api/relative/register.rs delete mode 100644 crates/test/src/env.rs delete mode 100644 crates/test/src/lib.rs delete mode 100644 docker-compose.yml delete mode 100644 docs/diagrams/diagram.excalidraw delete mode 100644 docs/diagrams/diagram.png delete mode 100644 fixtures/generate_mac.py create mode 100644 src/api.rs create mode 100644 src/api/ping.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore index ee4e61b..bca6f9b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,23 +3,5 @@ debug/ target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - # These are backup files generated by rustfmt **/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb - -# Development -/docker/* -!/docker/.gitkeep -!/docker/postgre -.env -access_token.txt -dump.sql - -# System Specific -.DS_Store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index c9657b1..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,114 +0,0 @@ -# Contributing to Rust Commune - -There are many ways to contribute to Commune Rust, including writing code, -openning issues, helping people, reproduce, or fix bugs that people have filed -and improving documentation. - -## Development Environment - -Commune Rust is written in The Rust Programming Language, you will have to -setup Rust in your machine to run the project locally. - -Tools like [**Justfile**][justfile] are recommended to improve DX and reduce -learning curve by running commands easily. - -[docker]: https://www.docker.com/get-started/ -[justfile]: https://github.com/casey/just -[rust]: https://rustup.rs - - -### Getting Started - -1. Create a copy of `.env.example` on `.env` - -```bash -cp .env.example .env -``` - -2. Generate `Synapse` server configuration - -```bash -just gen_synapse_conf -``` - -3. Run Synapse Server (and other containerized services) using Docker Compose -via: - -```bash -just backend -``` - -**When you are ready** - -Teardown services using `just stop`. -If you want to perform a complete cleanup use `just clear`. - -> **Warning** `just clear` will remove all containers and images. - -### Testing - -This application has 2 layers for tests: - -- `Unit`: Are usually inlined inside crates, and dont depend on any integration -- `E2E`: Lives in `test` crate and counts with the services that run the application - -#### Unit - -Unit tests can be executed via `cargo test -p `, this will run -every unit test. - -#### E2E - -You must run Docker services as for development. In order to avoid messing up -the development environment, its recommended to use the synapse setup from -`crates/test/fixtures/synapse` replacing it with `docker/synapse`. - -> Make sure the `.env` file is created from the contents on `.env.example` - -### Application Layout - -
- - Application Layout Overview -
- -The client, any HTTP Client, comunicates with the Commune Server which may or -may not communicate with Matrix's server _Synapse_ which runs along with its -database in a Docker container. - -#### Email Development - -Use [MJML Editor][mjml] and then render into HTML. Make sure variables use -Handlebars syntax (e.g. `{{name}}`). - -For local testing you can use something like: - -```bash -curl -s http://localhost:1080/email | grep -o -E "This is your verification code.{0,7}" | tail -1 | sed 's/^.*://' | awk '{$1=$1;print} -``` - -To get the very last email's verification code. - -> **Warning** Note that changes on email content will break this script - -[mjml]: https://mjml.io/try-it-live/99k8regCo_ - -#### Redis - -A Redis instance is used to keep in-memory short-lived data used certain server -operations such as storing verification codes. - -For this purpose Redis is served as part of the development stack on Docker. - -The `redis/redis-stack` image contains both Redis Stack server and RedisInsight, -you can use RedisInsight by pointing your browser to `localhost:8001`. - -#### Synapse - -There is an official [Synapse][1] image available at https://hub.docker.com/r/matrixdotorg/synapse -or at `ghcr.io/matrix-org/synapse` which can be used with the `docker-compose` -file available at [contrib/docker][2]. Further information on this including -configuration options is available in the README on hub.docker.com. - -[1]: https://matrix-org.github.io/synapse/latest/setup/installation.html#docker-images-and-ansible-playbooks -[2]: https://github.com/matrix-org/synapse/tree/develop/contrib/docker diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..93ad2ee --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,691 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "commune-rs" +version = "0.1.0" +dependencies = [ + "axum", + "serde", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "tokio" +version = "1.39.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +dependencies = [ + "backtrace", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index bbdf33d..7df2629 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,70 +1,47 @@ -[workspace.package] +[package] +name = "commune-rs" +edition = "2021" +version = "0.1.0" +readme = "README.md" +license = "Apache-2.0" + description = "Rust implementation of the Commune server." -edition = "2018" homepage = "https://commune.sh" -license = "Apache-2.0" -name = "commune" -readme = "README.md" repository = "https://github.com/commune-os/commune-rs" -rust-version = "1.75.0" -[workspace] -members = ["crates/core", "crates/matrix", "crates/router", "crates/test"] -default-members = ["crates/router"] -resolver = "2" +keywords = [ + "matrix", + "chat", + "messaging", + "federation", + "social", + "community", +] + +[dependencies] +axum = "0.7.5" +serde = { version = "1.0.209", features = ["derive"] } + +[dev-dependencies] +axum = { version = "0.7.5", features = ["macros"] } + +[profile.dev] +incremental = true +opt-level = 1 +lto = 'off' -[workspace.dependencies] -axum-extra = { version = "0.9.3", features = ["typed-header"] } -async-trait = "0.1.74" -# async-stream = "0.3.5" -bytes = "1.5.0" -email_address = { version = "0.2.4", features = ["serde", "serde_support"] } -figment = { version = "0.10.14", features = ["toml", "env"] } -hex = "0.4.3" -tokio-rustls = "0.25.0" -# futures = "0.3.30" -hmac = "0.12.1" -sha1 = "0.10.6" -anyhow = "1.0.75" -axum = { version = "0.7.4", features = ["tokio", "macros"] } -http = "0.2.11" -mime = "0.3.17" -mail-send = "0.4.7" -maud = "0.26.0" -headers = "0.4.0" -# openssl = { version = "0.10.63", features = ["vendored"] } -# openssl-sys = { version = "0.9.99", features = ["vendored"] } -reqwest = { version = "0.11.22", default-features = false, features = [ - "json", - "multipart", - "rustls", -] } -serde = "1.0.192" -serde_json = "1.0.114" -time = "0.3.34" -tokio = "1.34.0" -tracing = "0.1.40" -tracing-subscriber = "0.3.18" -url = "2.4.1" -rand = "0.8.5" -thiserror = "1.0.50" -validator = { version = "0.16", features = ["derive"] } +# NOTE: you might have to adjust the value for opt-level, as it +# comes with the drawback of less useful error messages for dependencies. +[profile.dev.package."*"] +opt-level = 3 -router = { workspace = true, path = "crates/router" } -matrix = { workspace = true, path = "crates/matrix" } -commune = { workspace = true, path = "crates/core" } +[profile.release] +incremental = true +opt-level = 3 +lto = 'thin' -ruma-events = { version = "0.27.11", default_features = false, features = [ - "html", - "markdown", -] } -ruma-common = { version = "0.12.0", default_features = false, features = [ - "api", - "rand", -] } -ruma-macros = { version = "0.12.0", default_features = false } -ruma-client = { version = "0.12.0", default_features = false } -ruma-identifiers-validation = { version = "0.9.3", default_features = false } +[profile.release.package."*"] +opt-level = 3 [workspace.lints.rust] unreachable_pub = "warn" @@ -89,17 +66,3 @@ unused_async = "warn" unused_results = "warn" unwrap_used = "warn" wildcard_imports = "warn" - -[profile.dev] -opt-level = 1 -incremental = true -lto = 'off' - -[profile.release] -lto = 'thin' -incremental = true - -[profile.release.build-override] -opt-level = 3 -[profile.release.package."*"] -opt-level = 3 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 7961240..0000000 --- a/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM arm64v8/alpine:3 - -COPY ./tmp/server /opt/commune - -WORKDIR app - -ENTRYPOINT ["/opt/commune"] diff --git a/Justfile b/Justfile deleted file mode 100644 index a7f5e8b..0000000 --- a/Justfile +++ /dev/null @@ -1,88 +0,0 @@ -set positional-arguments - -commit_sha := `git rev-parse --verify --short=7 HEAD` -target_release := "x86_64-unknown-linux-musl" - -# Lists all available commands -default: - just --list - -# Creates the `.env` file if it doesn't exist -# This indicates the first invocation of `just` so we also -# create the docker folders while we're at it -dotenv: - export DOCKER_USER="$(id -u):$(id -g)" && \ - cp -n .env.example .env || true && \ - mkdir -p docker/synapse || true - -# Dump database to a file -backup_db: - docker compose exec -T synapse_database \ - pg_dumpall -c -U synapse_user > ./dump.sql - -# Restore database from a file -restore_db: - cat ./dump.sql | docker compose exec -T synapse_database \ - psql -U synapse_user -d synapse - -# Nuke database -nuke_db: - docker compose exec -T synapse_database \ - psql -U synapse_user -d synapse -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;" - -# Generates the synapse configuration file and saves it -gen_synapse_conf: dotenv - docker run -i --rm \ - -u "$(id -u):$(id -g)" \ - -v ./docker/synapse:/data \ - --env-file .env \ - matrixdotorg/synapse:v1.96.1 generate - -# Generates a de-facto admin user -gen_synapse_admin: dotenv - docker compose exec -i synapse \ - register_new_matrix_user http://localhost:8008 \ - -c /data/homeserver.yaml \ - -u admin \ - -p admin \ - -a - -# Retrieves admin access token uses de-facto admin user and Development Database Credentials -get_access_token: - sed -i "s/COMMUNE_SYNAPSE_ADMIN_TOKEN='.*'/COMMUNE_SYNAPSE_ADMIN_TOKEN='$( \ - curl -sS -d '{"type":"m.login.password", "user":"admin", "password":"admin"}' \ - http://localhost:8008/_matrix/client/v3/login | jq --raw-output '.access_token' \ - )'/" .env - -# Runs backend dependency services -backend *args='': dotenv - docker compose up --build $1 - -# Stops backend dependency services -stop: - docker compose down - -# Removes oll Docker related config, volumes and containers for this project -clear: stop - docker compose rm --all --force --volumes --stop - docker volume rm commune_synapse_database || true - -# Runs all the tests from the `test` package. Optionally runs a single one if name pattern is provided -e2e *args='': - cargo test --package test -- --nocapture --test-threads=1 $1 - -# Builds the Server binary used in the Docker Image -docker_build_server: - cargo zigbuild --target {{target_release}} --release -p server - -# Builds the Docker image for the backend -docker_build_image: docker_build_server - mkdir tmp/ - cp ./target/{{target_release}}/release/server ./tmp/server - chmod +x ./tmp/server - docker build -t "commune:{{commit_sha}}-{{target_release}}" . - -# Publishes the Docker image to the GitHub Container Registry -docker_publish_image: - docker tag commune:{{commit_sha}}-{{target_release}} ghcr.io/commune-os/commune:{{commit_sha}}-{{target_release}} - docker push ghcr.io/commune-os/commune:{{commit_sha}}-{{target_release}} diff --git a/commune-example.toml b/commune-example.toml deleted file mode 100644 index 38f633e..0000000 --- a/commune-example.toml +++ /dev/null @@ -1,23 +0,0 @@ -registration_verification = false -public_loopback = false -port = 6421 -tls = true - -# Either one works but not both -blocked_domains = [] -# allowed_domains = ['gmail.com', 'outlook.com'] - -# `X-Forwarded-For` header -# xff = false - -[matrix] -server_name = "matrix.localhost" -host = "http://0.0.0.0:8008" -admin_token = "syt_YWRtaW4_FllbTksPWcQaDRUVVcYR_3LJQZ2" -shared_registration_secret = "m@;wYOUOh0f:CH5XA65sJB1^q01~DmIriOysRImot,OR_vzN&B" - -[mail] -host = "smtp://0.0.0.0:1025" -username = "" -password = "" -tls = false diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml deleted file mode 100644 index a6961dc..0000000 --- a/crates/core/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "core" -version = "0.0.0" -edition = "2021" -publish = false - -[lib] -name = "commune" -path = "src/lib.rs" - -[dependencies] -# Workspace Dependencies -anyhow = { workspace = true } -axum = { workspace = true } -rand = { workspace = true } -email_address = { workspace = true } -thiserror = { workspace = true } -validator = { workspace = true, features = ["derive"] } -http = { workspace = true } -mail-send = { workspace = true } -maud = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -tracing = { workspace = true } -figment = { workspace = true } -url = { workspace = true, features = ["serde"] } -tokio = { workspace = true, features = ["full"] } -headers = { workspace = true } -tokio-rustls = { workspace = true } - -# Local Dependencies -matrix = { path = "../matrix", features = ["client"] } diff --git a/crates/core/src/account.rs b/crates/core/src/account.rs deleted file mode 100644 index ce3b5ea..0000000 --- a/crates/core/src/account.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod email; -pub mod login; -pub mod logout; -pub mod password; -pub mod register; -pub mod token; -pub mod username; -pub mod whoami; diff --git a/crates/core/src/account/email.rs b/crates/core/src/account/email.rs deleted file mode 100644 index c251b8e..0000000 --- a/crates/core/src/account/email.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::time::{SystemTime, UNIX_EPOCH}; - -use email_address::EmailAddress; -use matrix::admin::registration_tokens::new::*; -use rand::{distributions::Uniform, prelude::Distribution}; - -use crate::{commune, error::Result}; - -pub async fn service(address: EmailAddress) -> Result<()> { - let uni = Uniform::new('0', '9'); - let token: String = uni.sample_iter(rand::thread_rng()).take(6).collect(); - - let req = Request::new( - token.clone(), - 1, - SystemTime::now() - .duration_since(UNIX_EPOCH) - // panics below should never happen - .expect("system time overflow") - .as_millis() - .try_into() - .expect("system time overflow"), - ); - - commune() - .send_matrix_request(req, Some(&commune().config.matrix.admin_token.inner())) - .await?; - - commune().send_email_verification(address, token).await?; - - Ok(()) -} diff --git a/crates/core/src/account/login.rs b/crates/core/src/account/login.rs deleted file mode 100644 index 7a31e83..0000000 --- a/crates/core/src/account/login.rs +++ /dev/null @@ -1,21 +0,0 @@ -use matrix::client::{login::*, uiaa::UserIdentifier}; - -use crate::{commune, error::Result, util::secret::Secret}; - -pub async fn service(username: impl Into, password: &Secret) -> Result { - let req = Request::new( - LoginType::Password { - password: password.inner(), - }, - Some(UserIdentifier::User { - user: username.into(), - }), - "commune".to_owned(), - Some(true), - ); - - commune() - .send_matrix_request(req, None) - .await - .map_err(Into::into) -} diff --git a/crates/core/src/account/logout.rs b/crates/core/src/account/logout.rs deleted file mode 100644 index 972d9ef..0000000 --- a/crates/core/src/account/logout.rs +++ /dev/null @@ -1,12 +0,0 @@ -use matrix::client::logout::root::*; - -use crate::{commune, error::Result}; - -pub async fn service(access_token: impl AsRef) -> Result { - let req = Request::new(); - - commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await - .map_err(Into::into) -} diff --git a/crates/core/src/account/password.rs b/crates/core/src/account/password.rs deleted file mode 100644 index 7e7783a..0000000 --- a/crates/core/src/account/password.rs +++ /dev/null @@ -1,20 +0,0 @@ -use matrix::{client::account::password::*, ruma_common::UserId}; - -use crate::{commune, error::Result, util::secret::Secret}; - -pub async fn service( - access_token: impl AsRef, - username: impl Into, - old_password: Secret, - new_password: Secret, -) -> Result { - let server_name = &crate::commune().config.matrix.server_name; - let user_id = UserId::parse_with_server_name(username.into(), server_name)?; - - let req = Request::new(new_password.inner()).with_password(user_id, old_password.inner()); - - commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await - .map_err(Into::into) -} diff --git a/crates/core/src/account/register.rs b/crates/core/src/account/register.rs deleted file mode 100644 index b8b384e..0000000 --- a/crates/core/src/account/register.rs +++ /dev/null @@ -1,53 +0,0 @@ -use http::StatusCode; -use matrix::{ - client::{ - register::root::*, - uiaa::{Auth, AuthData, AuthType, Dummy, UiaaResponse}, - }, - ruma_client::Error::FromHttpResponse, - ruma_common::api::error::{FromHttpResponseError, MatrixError, MatrixErrorBody}, -}; - -use crate::{commune, error::Result, util::secret::Secret}; - -pub async fn service(username: impl Into, password: Secret) -> Result { - let req = Request::new( - username.into(), - password.inner(), - Some("commune".to_owned()), - None, - None, - ); - - let mut retry_req = req.clone(); - - match commune().send_matrix_request(req, None).await { - Ok(resp) => Ok(resp), - Err(e) => match e { - FromHttpResponse(FromHttpResponseError::Server(MatrixError { - status_code: StatusCode::UNAUTHORIZED, - body: MatrixErrorBody::Json(ref body), - })) => { - let UiaaResponse { flows, session, .. } = - serde_json::from_value::(body.clone()).unwrap(); - - match flows.as_slice() { - [value] => match value.stages.as_slice() { - [AuthType::Dummy] => { - retry_req.auth = Some(Auth::new(AuthData::Dummy(Dummy {}), session)); - - commune() - .send_matrix_request(retry_req, None) - .await - .map_err(Into::into) - } - _ => Err(e.into()), - }, - _ => Err(e.into()), - } - } - - _ => Err(e.into()), - }, - } -} diff --git a/crates/core/src/account/token.rs b/crates/core/src/account/token.rs deleted file mode 100644 index cc85ac4..0000000 --- a/crates/core/src/account/token.rs +++ /dev/null @@ -1,12 +0,0 @@ -use matrix::client::register::token::validity::*; - -use crate::{commune, error::Result}; - -pub async fn service(access_token: impl AsRef) -> Result { - let req = Request::new(access_token.as_ref().to_owned()); - - commune() - .send_matrix_request(req, None) - .await - .map_err(Into::into) -} diff --git a/crates/core/src/account/username.rs b/crates/core/src/account/username.rs deleted file mode 100644 index 37528d7..0000000 --- a/crates/core/src/account/username.rs +++ /dev/null @@ -1,12 +0,0 @@ -use matrix::client::register::available::*; - -use crate::{commune, error::Result}; - -pub async fn service(username: impl Into) -> Result { - let req = Request::new(username.into()); - - commune() - .send_matrix_request(req, None) - .await - .map_err(Into::into) -} diff --git a/crates/core/src/account/whoami.rs b/crates/core/src/account/whoami.rs deleted file mode 100644 index 908e1bc..0000000 --- a/crates/core/src/account/whoami.rs +++ /dev/null @@ -1,12 +0,0 @@ -use matrix::client::account::whoami::*; - -use crate::{commune, error::Result}; - -pub async fn service(access_token: impl AsRef) -> Result { - let req = Request::new(); - - commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await - .map_err(Into::into) -} diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs deleted file mode 100644 index 57b5f82..0000000 --- a/crates/core/src/config.rs +++ /dev/null @@ -1,34 +0,0 @@ -use matrix::ruma_common::OwnedServerName; -use serde::Deserialize; -use url::Url; - -use crate::util::secret::Secret; - -#[derive(Debug, Deserialize)] -pub struct Config { - pub registration_verification: bool, - pub public_loopback: bool, - pub port: Option, - - pub allowed_domains: Option>, - pub blocked_domains: Option>, - - pub matrix: Matrix, - pub mail: SMTP, -} - -#[derive(Debug, Deserialize)] -pub struct SMTP { - pub host: Url, - pub username: Option, - pub password: Secret, - pub tls: bool, -} - -#[derive(Debug, Deserialize)] -pub struct Matrix { - pub host: Url, - pub server_name: OwnedServerName, - pub admin_token: Secret, - pub shared_registration_secret: Secret, -} diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs deleted file mode 100644 index 7134727..0000000 --- a/crates/core/src/error.rs +++ /dev/null @@ -1,32 +0,0 @@ -use axum::{http::StatusCode, response::IntoResponse}; -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -#[non_exhaustive] -pub enum Error { - #[error("forwarding a Matrix request failed: {0}")] - Matrix(#[from] matrix::HandleError), - - #[error("instance does not allow email address originating from this domain")] - EmailDomain, - - #[error("failed to validate identifier: {0}")] - InvalidIdentifier(#[from] matrix::ruma_identifiers_validation::Error), - - #[error("an IO operation failed: {0}")] - IO(#[from] std::io::Error), - - #[error(transparent)] - SMTP(#[from] mail_send::Error), - - #[error(transparent)] - Unknown(#[from] anyhow::Error), -} - -impl IntoResponse for Error { - fn into_response(self) -> axum::response::Response { - (StatusCode::BAD_REQUEST, self.to_string()).into_response() - } -} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs deleted file mode 100644 index 2ccd121..0000000 --- a/crates/core/src/lib.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! This library deals with our core logic, such as authorizing user -//! interactions, forwarding regular events and constructing custom requests. - -pub mod config; -pub mod error; -pub mod util; - -pub mod account; -pub mod profile; - -use std::sync::RwLock; - -use config::Config; -use email_address::EmailAddress; -use figment::{ - providers::{Env, Format, Toml}, - Figment, -}; -use mail_send::{mail_builder::MessageBuilder, SmtpClientBuilder}; -use matrix::{ - ruma_client::{HttpClientExt, ResponseResult}, - ruma_common::api::{OutgoingRequest, SendAccessToken}, -}; - -static COMMUNE: RwLock> = RwLock::new(None); - -pub struct Commune { - pub config: Config, - client: matrix::Client, - // smtp: SmtpClient>, -} - -pub async fn init() { - let mut commune = COMMUNE.write().unwrap(); - - let config = Figment::new() - .merge(Toml::file( - Env::var("COMMUNE_CONFIG").unwrap_or("./commune-example.toml".to_owned()), - )) - .extract::() - .unwrap(); - - if config - .allowed_domains - .as_ref() - .is_some_and(|v| !v.is_empty()) - && config - .blocked_domains - .as_ref() - .is_some_and(|v| !v.is_empty()) - { - panic!("config can only contain either allowed or blocked domains"); - } - - let client = matrix::Client::default(); - - *commune = Some(Box::leak(Box::new(Commune { config, client }))); -} - -pub fn commune() -> &'static Commune { - COMMUNE - .read() - .unwrap() - .expect("commune should be initialized at this point") -} - -impl Commune { - pub async fn send_matrix_request( - &self, - request: R, - access_token: Option<&str>, - ) -> ResponseResult { - let at = match access_token { - Some(at) => SendAccessToken::Always(at), - None => SendAccessToken::None, - }; - - self.client - .send_matrix_request::(self.config.matrix.host.as_str(), at, &[], request) - .await - } - - pub async fn send_email_verification( - &self, - address: EmailAddress, - token: impl Into, - ) -> mail_send::Result<()> { - let config = &commune().config; - - let password = config.mail.password.inner(); - let username = config - .mail - .username - .as_deref() - .unwrap_or(&password) - .to_owned(); - let host = &config.mail.host; - - let mut smtp = SmtpClientBuilder::new( - host.host_str() - .expect("failed to extract host from email configuration"), - 587, - ) - .implicit_tls(false) - .credentials((username.as_str(), password.as_str())) - .connect() - .await?; - - let token = token.into(); - let from = format!("commune@{host}"); - let html = format!( - "

Thanks for signing up.\n\nUse this code to finish verifying your \ - email:\n{token}

" - ); - let text = format!( - "Thanks for signing up.\n\nUse this code to finish verifying your email:\n{token}" - ); - - let message = MessageBuilder::new() - .from(("Commune", from.as_str())) - .to(vec![address.as_str()]) - .subject("Email Verification Code") - .html_body(html.as_str()) - .text_body(text.as_str()); - - smtp.send(message).await - } -} diff --git a/crates/core/src/profile.rs b/crates/core/src/profile.rs deleted file mode 100644 index bea0178..0000000 --- a/crates/core/src/profile.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod avatar; -pub mod display_name; diff --git a/crates/core/src/profile/avatar.rs b/crates/core/src/profile/avatar.rs deleted file mode 100644 index 078c6cb..0000000 --- a/crates/core/src/profile/avatar.rs +++ /dev/null @@ -1,41 +0,0 @@ -pub mod get { - use matrix::{client::profile::avatar_url::get::*, ruma_common::OwnedUserId}; - - use crate::{commune, error::Result}; - - pub async fn service(user_id: impl Into) -> Result { - let req = Request::new(user_id.into()); - - commune() - .send_matrix_request(req, None) - .await - .map_err(Into::into) - } -} - -pub mod update { - use matrix::{ - client::{account::whoami, profile::avatar_url::update::*}, - ruma_common::OwnedMxcUri, - }; - - use crate::{commune, error::Result}; - - pub async fn service( - access_token: impl AsRef, - mxc_uri: impl Into, - ) -> Result { - let req = whoami::Request::new(); - - let whoami::Response { user_id, .. } = commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await?; - - let req = Request::new(user_id, mxc_uri.into()); - - commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await - .map_err(Into::into) - } -} diff --git a/crates/core/src/profile/display_name.rs b/crates/core/src/profile/display_name.rs deleted file mode 100644 index 730ac70..0000000 --- a/crates/core/src/profile/display_name.rs +++ /dev/null @@ -1,38 +0,0 @@ -pub mod get { - use matrix::{client::profile::display_name::get::*, ruma_common::OwnedUserId}; - - use crate::{commune, error::Result}; - - pub async fn service(user_id: impl Into) -> Result { - let req = Request::new(user_id.into()); - - commune() - .send_matrix_request(req, None) - .await - .map_err(Into::into) - } -} - -pub mod update { - use matrix::client::{account::whoami, profile::display_name::update::*}; - - use crate::{commune, error::Result}; - - pub async fn service( - access_token: impl AsRef, - display_name: impl Into, - ) -> Result { - let req = whoami::Request::new(); - - let whoami::Response { user_id, .. } = commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await?; - - let req = Request::new(user_id, display_name.into()); - - commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await - .map_err(Into::into) - } -} diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs deleted file mode 100644 index 73b12db..0000000 --- a/crates/core/src/util.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod secret; diff --git a/crates/core/src/util/secret.rs b/crates/core/src/util/secret.rs deleted file mode 100644 index b1eb440..0000000 --- a/crates/core/src/util/secret.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::fmt::{Debug, Display}; - -use rand::{distributions::Uniform, Rng}; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize)] -pub struct Secret(String); - -// is this necessary? -impl Serialize for Secret { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.inner().serialize(serializer) - } -} - -impl Secret { - #[inline] - pub fn new(s: impl Into) -> Self { - Self(s.into()) - } - - #[inline] - pub fn inner(&self) -> String { - self.0.clone() - } -} - -impl Debug for Secret { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(format!("{self}").as_str()) - } -} - -impl Display for Secret { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let braille_range = Uniform::new('\u{2800}', '\u{28FF}'); - let s: String = rand::thread_rng() - .sample_iter(braille_range) - .take(self.0.len()) - .collect(); - - f.write_str(s.as_str()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn do_not_display_value() { - let secret = Secret::new("secret"); - let display = format!("{}", secret); - - assert_eq!(display, "[REDACTED]"); - } - - #[test] - fn do_not_debug_value() { - let secret = Secret::new("secret"); - let display = format!("{:?}", secret); - - assert_eq!(display, "[REDACTED]"); - } - - #[test] - fn retrieves_original() { - let secret = Secret::new("secret"); - let value = secret.inner(); - - assert_eq!(value, "secret".into()); - } -} diff --git a/crates/matrix/Cargo.toml b/crates/matrix/Cargo.toml deleted file mode 100644 index 25a1738..0000000 --- a/crates/matrix/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "matrix" -version = "0.0.0" -edition = "2021" -publish = false - -[lib] -name = "matrix" -path = "src/lib.rs" - -[dependencies] -ruma-events = { workspace = true } -ruma-common = { workspace = true } -ruma-macros = { workspace = true } -ruma-client = { workspace = true } -ruma-identifiers-validation = { workspace = true } - -# Workspace Dependencies -mime = { workspace = true } -reqwest = { workspace = true, features = ["json"] } -serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -tracing = { workspace = true } -sha1 = { workspace = true } -url = { workspace = true, features = ["serde"] } -hex = { workspace = true } -hmac = { workspace = true } -http = { workspace = true } -bytes = { workspace = true } -async-trait = { workspace = true } - -[features] -client = [] -server = [] diff --git a/crates/matrix/src/admin.rs b/crates/matrix/src/admin.rs deleted file mode 100644 index 2608ca2..0000000 --- a/crates/matrix/src/admin.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! This module is the root of the admin API. -//! -//! reference: https://matrix-org.github.io/synapse/latest/usage/administration/admin_api/index.html - -pub mod registration_tokens; -// pub mod room; -// pub mod session; -// pub mod user; diff --git a/crates/matrix/src/admin/registration_tokens.rs b/crates/matrix/src/admin/registration_tokens.rs deleted file mode 100644 index 9d52a2b..0000000 --- a/crates/matrix/src/admin/registration_tokens.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod new; diff --git a/crates/matrix/src/admin/registration_tokens/new.rs b/crates/matrix/src/admin/registration_tokens/new.rs deleted file mode 100644 index 7849c07..0000000 --- a/crates/matrix/src/admin/registration_tokens/new.rs +++ /dev/null @@ -1,38 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: POST, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/register/new", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - pub token: String, - - pub uses_allowed: usize, - - pub expiry_time: usize, -} - -impl Request { - pub fn new(token: String, uses_allowed: usize, expiry_time: usize) -> Self { - Self { - token, - uses_allowed, - expiry_time, - } - } -} - -// Same fields as above are returned but we only -// care about knowing whether the call was successful. -#[response(error = crate::Error)] -pub struct Response {} diff --git a/crates/matrix/src/admin/room.rs b/crates/matrix/src/admin/room.rs deleted file mode 100644 index 38a3738..0000000 --- a/crates/matrix/src/admin/room.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! This module contains handlers for managing rooms. -//! -//! reference: https://matrix-org.github.io/synapse/latest/admin_api/rooms.html - -use ruma_common::{ - room::RoomType, EventEncryptionAlgorithm, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, - OwnedUserId, RoomVersionId, -}; -use ruma_events::room::{history_visibility::HistoryVisibility, join_rules::JoinRule}; -use serde::Deserialize; - -pub mod delete_room; -pub mod get_members; -pub mod get_room; -pub mod get_rooms; -pub mod get_state; - -#[derive(Clone, Debug, Deserialize)] -pub struct Room { - pub room_id: OwnedRoomId, - - pub canonical_alias: Option, - - pub avatar: Option, - - pub name: Option, - - pub joined_members: u64, - - pub joined_local_members: u64, - - pub version: RoomVersionId, - - pub creator: OwnedUserId, - - pub encryption: Option, - - pub federatable: bool, - - pub public: bool, - - pub join_rules: Option, - - pub history_visibility: Option, - - pub state_events: u64, - - pub room_type: Option, - - #[serde(flatten)] - pub details: Option, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct RoomDetails { - pub topic: Option, - - pub forgotten: bool, -} diff --git a/crates/matrix/src/admin/room/delete_room.rs b/crates/matrix/src/admin/room/delete_room.rs deleted file mode 100644 index 578a334..0000000 --- a/crates/matrix/src/admin/room/delete_room.rs +++ /dev/null @@ -1,47 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedRoomId, OwnedUserId, -}; -use serde::Serialize; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: DELETE, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v2/rooms/:room_id", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub room_id: OwnedRoomId, - - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub new_room: Option, - - pub block: bool, - - #[serde(skip_serializing_if = "ruma_common::serde::is_true")] - pub purge: bool, - - pub force_purge: bool, -} - -#[response(error = crate::Error)] -pub struct Response { - pub delete_id: String, -} - -#[derive(Clone, Debug, Serialize)] -pub struct NewRoomParams { - pub creator: OwnedUserId, - - #[serde(skip_serializing_if = "String::is_empty")] - pub name: String, - - #[serde(skip_serializing_if = "String::is_empty")] - pub message: String, -} diff --git a/crates/matrix/src/admin/room/forward_extremities/delete.rs b/crates/matrix/src/admin/room/forward_extremities/delete.rs deleted file mode 100644 index e69de29..0000000 diff --git a/crates/matrix/src/admin/room/forward_extremities/get.rs b/crates/matrix/src/admin/room/forward_extremities/get.rs deleted file mode 100644 index e69de29..0000000 diff --git a/crates/matrix/src/admin/room/get_members.rs b/crates/matrix/src/admin/room/get_members.rs deleted file mode 100644 index 79cd4e7..0000000 --- a/crates/matrix/src/admin/room/get_members.rs +++ /dev/null @@ -1,27 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedRoomId, OwnedUserId, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/rooms/:room_id/members", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub room_id: OwnedRoomId, -} - -#[response(error = crate::Error)] -pub struct Response { - pub members: Vec, - - pub total: u64, -} diff --git a/crates/matrix/src/admin/room/get_room.rs b/crates/matrix/src/admin/room/get_room.rs deleted file mode 100644 index 967f577..0000000 --- a/crates/matrix/src/admin/room/get_room.rs +++ /dev/null @@ -1,27 +0,0 @@ -use super::Room; -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedRoomId, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/rooms/:room_id", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub room_id: OwnedRoomId, -} - -#[response(error = crate::Error)] -pub struct Response { - #[ruma_api(body)] - pub room: Room, -} diff --git a/crates/matrix/src/admin/room/get_rooms.rs b/crates/matrix/src/admin/room/get_rooms.rs deleted file mode 100644 index 08a792c..0000000 --- a/crates/matrix/src/admin/room/get_rooms.rs +++ /dev/null @@ -1,83 +0,0 @@ -use ruma_common::{ - api::{request, response, Direction, Metadata}, - metadata, -}; -use serde::Serialize; - -use super::Room; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/rooms", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[serde(default)] - #[ruma_api(query)] - pub from: u64, - - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub limit: Option, - - #[ruma_api(query)] - pub order_by: OrderBy, - - #[ruma_api(query)] - pub direction: Direction, - - #[serde(skip_serializing_if = "String::is_empty")] - #[ruma_api(query)] - pub search_term: String, -} - -#[response(error = crate::Error)] -pub struct Response { - pub rooms: Vec, - - pub offset: u64, - - #[serde(rename = "total_rooms")] - pub total: u64, - - pub next_batch: Option, - - pub prev_batch: Option, -} - -#[derive(Clone, Default, Debug, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum OrderBy { - #[default] - Name, - - CanonicalAlias, - - JoinedMembers, - - JoinedLocalMembers, - - Version, - - Creator, - - Encryption, - - Federatable, - - Public, - - JoinRules, - - GuestAccess, - - HistoryVisibility, - - StateEvents, -} diff --git a/crates/matrix/src/admin/room/get_state.rs b/crates/matrix/src/admin/room/get_state.rs deleted file mode 100644 index 6cf649b..0000000 --- a/crates/matrix/src/admin/room/get_state.rs +++ /dev/null @@ -1,36 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedRoomId, -}; -use serde::Deserialize; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/rooms/:room_id/state", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub room_id: OwnedRoomId, -} - -#[response(error = crate::Error)] -pub struct Response { - pub state: Vec, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct State { - #[serde(rename = "type")] - pub kind: String, - - pub state_key: String, - - pub etc: bool, -} diff --git a/crates/matrix/src/admin/session.rs b/crates/matrix/src/admin/session.rs deleted file mode 100644 index 10d725f..0000000 --- a/crates/matrix/src/admin/session.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! This module contains handlers for user registration. -//! -//! reference: https://matrix-org.github.io/synapse/latest/admin_api/register_api.html - -use hmac::Mac; -use serde::Serialize; - -pub mod get_nonce; -pub mod register; - -#[derive(Clone, Debug, Serialize)] -pub struct Hmac { - inner: Vec, -} - -impl Hmac { - pub fn new( - shared_secret: &str, - nonce: &str, - username: &str, - password: &str, - admin: bool, - ) -> Result { - let mut mac = hmac::Hmac::::new_from_slice(shared_secret.as_bytes())?; - let admin = match admin { - true => "admin", - false => "notadmin", - }; - - mac.update( - &[nonce, username, password, admin] - .map(str::as_bytes) - .join(&0x00), - ); - - let result = mac.finalize().into_bytes(); - - Ok(Self { - inner: result.to_vec(), - }) - } - - pub fn get(&self) -> String { - hex::encode(&self.inner) - } -} diff --git a/crates/matrix/src/admin/session/get_nonce.rs b/crates/matrix/src/admin/session/get_nonce.rs deleted file mode 100644 index 0388987..0000000 --- a/crates/matrix/src/admin/session/get_nonce.rs +++ /dev/null @@ -1,22 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/register", - } -}; - -#[request(error = crate::Error)] -pub struct Request {} - -#[response(error = crate::Error)] -pub struct Response { - pub nonce: String, -} diff --git a/crates/matrix/src/admin/session/register.rs b/crates/matrix/src/admin/session/register.rs deleted file mode 100644 index 4838353..0000000 --- a/crates/matrix/src/admin/session/register.rs +++ /dev/null @@ -1,43 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedDeviceId, OwnedServerName, OwnedUserId, -}; - -use super::Hmac; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/register", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - pub nonce: String, - - pub username: String, - - pub password: String, - - #[serde(skip_serializing_if = "String::is_empty")] - pub displayname: String, - - pub admin: bool, - - pub hmac: Hmac, -} - -#[response(error = crate::Error)] -pub struct Response { - pub access_token: String, - - pub user_id: OwnedUserId, - - pub home_server: OwnedServerName, - - pub device_id: OwnedDeviceId, -} diff --git a/crates/matrix/src/admin/user.rs b/crates/matrix/src/admin/user.rs deleted file mode 100644 index 3417bb5..0000000 --- a/crates/matrix/src/admin/user.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! This module contains handlers for managing users. -//! -//! reference: https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html - -use ruma_common::{thirdparty::ThirdPartyIdentifier, OwnedMxcUri, OwnedUserId}; -use serde::{Deserialize, Serialize}; - -pub mod get_user; -pub mod get_user_by_3pid; -pub mod get_users; -pub mod set_user; - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct User { - #[serde(rename = "name")] - pub user_id: OwnedUserId, - - pub displayname: Option, - - pub avatar_url: Option, - - pub threepids: Vec, - - pub external_ids: Vec, - - pub admin: bool, - - pub deactivated: bool, - - #[serde(skip_serializing)] - pub erased: bool, - - #[serde(skip_serializing)] - pub shadow_banned: bool, - - #[serde(skip_serializing)] - pub creation_ts: u64, - - #[serde(skip_serializing)] - pub consent_server_notice_sent: Option, - - #[serde(skip_serializing)] - pub consent_ts: Option, - - pub locked: bool, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct ExternalId { - pub auth_provider: String, - - pub external_id: String, -} diff --git a/crates/matrix/src/admin/user/get_user.rs b/crates/matrix/src/admin/user/get_user.rs deleted file mode 100644 index da302be..0000000 --- a/crates/matrix/src/admin/user/get_user.rs +++ /dev/null @@ -1,28 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedUserId, -}; - -use super::User; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v2/users/:user_id", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub user_id: OwnedUserId, -} - -#[response(error = crate::Error)] -pub struct Response { - #[ruma_api(body)] - pub user: User, -} diff --git a/crates/matrix/src/admin/user/get_user_by_3pid.rs b/crates/matrix/src/admin/user/get_user_by_3pid.rs deleted file mode 100644 index 2263ba7..0000000 --- a/crates/matrix/src/admin/user/get_user_by_3pid.rs +++ /dev/null @@ -1,32 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, - thirdparty::Medium, -}; - -use super::User; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v1/threepid/:medium/users/:address", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub medium: Medium, - - #[ruma_api(path)] - pub address: String, -} - -#[response(error = crate::Error)] -pub struct Response { - #[ruma_api(body)] - pub user: User, -} diff --git a/crates/matrix/src/admin/user/get_users.rs b/crates/matrix/src/admin/user/get_users.rs deleted file mode 100644 index ad094b8..0000000 --- a/crates/matrix/src/admin/user/get_users.rs +++ /dev/null @@ -1,84 +0,0 @@ -use ruma_common::{ - api::{request, response, Direction, Metadata}, - metadata, OwnedUserId, -}; -use serde::Serialize; - -use super::User; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v2/users", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub user_id: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub name: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub admins: Option, - - #[serde(skip_serializing_if = "ruma_common::serde::is_default")] - #[ruma_api(query)] - pub deactivated: bool, - - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub limit: Option, - - #[serde(skip_serializing_if = "ruma_common::serde::is_default")] - #[ruma_api(query)] - pub from: u64, - - #[serde(skip_serializing_if = "ruma_common::serde::is_default")] - #[ruma_api(query)] - pub order_by: OrderBy, - - #[serde(skip_serializing_if = "ruma_common::serde::is_default")] - #[ruma_api(query)] - pub dir: Direction, -} - -#[response(error = crate::Error)] -pub struct Response { - pub users: Vec, - - pub next_token: String, - - pub total: u64, -} - -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] -#[allow(dead_code)] -pub enum OrderBy { - #[default] - Name, - - Admin, - - UserType, - - Deactivated, - - ShadowBanned, - - Displayname, - - AvatarUrl, - - CreationTs, - - LastSeenTs, -} diff --git a/crates/matrix/src/admin/user/set_user.rs b/crates/matrix/src/admin/user/set_user.rs deleted file mode 100644 index 83868f1..0000000 --- a/crates/matrix/src/admin/user/set_user.rs +++ /dev/null @@ -1,28 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedUserId, -}; - -use super::User; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: PUT, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_synapse/admin/v2/users/:user_id", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub user_id: OwnedUserId, - - #[ruma_api(body)] - pub user: User, -} - -#[response(error = crate::Error)] -pub struct Response {} diff --git a/crates/matrix/src/client-backup/account.rs.bk.bk b/crates/matrix/src/client-backup/account.rs.bk.bk deleted file mode 100644 index d48afb7..0000000 --- a/crates/matrix/src/client-backup/account.rs.bk.bk +++ /dev/null @@ -1,3 +0,0 @@ -pub mod create; -pub mod password; -pub mod whoami; diff --git a/crates/matrix/src/client-backup/events.rs.bk.bk.bk b/crates/matrix/src/client-backup/events.rs.bk.bk.bk deleted file mode 100644 index 2953b2b..0000000 --- a/crates/matrix/src/client-backup/events.rs.bk.bk.bk +++ /dev/null @@ -1,310 +0,0 @@ -use anyhow::Result; -use ruma_common::{serde::Raw, EventId, OwnedEventId, OwnedTransactionId, RoomId}; - -use ruma_events::{ - relation::RelationType, AnyMessageLikeEvent, AnyStateEvent, AnyStateEventContent, - AnyTimelineEvent, MessageLikeEventContent, MessageLikeEventType, StateEventContent, - StateEventType, -}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use tracing::instrument; - -use crate::{admin::resources::room::Direction, error::MatrixError, Client}; - -pub struct EventsService; - -#[derive(Debug, Default, Clone, Serialize)] -pub struct GetMessagesQuery { - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub to: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - pub dir: Direction, - - #[serde(skip_serializing_if = "String::is_empty")] - pub filter: String, -} - -#[derive(Debug, Default, Clone, Serialize)] -pub struct GetRelationsQuery { - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub to: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - pub dir: Direction, -} - -#[derive(Debug, Deserialize)] -pub struct GetMessagesResponse { - pub chunk: Vec>, - pub start: String, - pub end: String, - pub state: Option>>, -} - -#[derive(Debug, Deserialize)] -#[serde(transparent)] -pub struct GetStateResponse(pub Vec>); - -#[derive(Debug, Deserialize)] -pub struct GetRelationsResponse { - pub chunk: Vec>, - pub prev_batch: Option, - pub next_batch: Option, -} - -#[derive(Debug, Default, Serialize)] -pub struct SendRedactionBody { - #[serde(skip_serializing_if = "String::is_empty")] - pub reason: String, -} - -#[derive(Debug, Deserialize)] -pub struct SendMessageResponse { - pub event_id: OwnedEventId, -} - -#[derive(Debug, Deserialize)] -pub struct SendStateResponse { - pub event_id: OwnedEventId, -} - -#[derive(Debug, Deserialize)] -pub struct SendRedactionResponse { - pub event_id: OwnedEventId, -} - -impl EventsService { - #[instrument(skip(client, access_token))] - pub async fn get_event( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_id: &EventId, - ) -> Result> { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get(format!( - "/_matrix/client/v3/rooms/{room_id}/event/{event_id}", - room_id = room_id, - event_id = event_id, - )) - .await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_messages( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - query: GetMessagesQuery, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get_query( - format!( - "/_matrix/client/v3/rooms/{room_id}/messages", - room_id = room_id, - ), - &query, - ) - .await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_state( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get(format!( - "/_matrix/client/v3/rooms/{room_id}/state", - room_id = room_id, - )) - .await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_state_content( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_type: StateEventType, - state_key: Option, - ) -> Result> { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let mut path = format!( - "/_matrix/client/v3/rooms/{room_id}/state/{event_type}", - room_id = room_id, - event_type = event_type, - ); - - if let Some(state_key) = state_key { - path.push_str(&format!("/{state_key}", state_key = state_key)) - } - - let resp = tmp.get(path).await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_relations( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_id: &EventId, - rel_type: Option>, - event_type: Option, - query: GetRelationsQuery, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let mut path = format!( - "/_matrix/client/v3/rooms/{room_id}/relations/{event_id}", - room_id = room_id, - event_id = event_id, - ); - - if let Some(rel_type) = rel_type { - path.push_str(&format!( - "/{rel_type}", - rel_type = rel_type - .as_ref() - .map_or("m.in_reply_to".into(), ToString::to_string) - )); - - if let Some(event_type) = event_type { - path.push_str(&format!("/{event_type}", event_type = event_type)) - } - } - - let resp = tmp.get_query(path, &query).await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token, body))] - pub async fn send_message( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - txn_id: OwnedTransactionId, - body: T, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .put_json( - format!( - "/_matrix/client/v3/rooms/{room_id}/send/{event_type}/{txn_id}", - room_id = room_id, - event_type = body.event_type(), - txn_id = txn_id, - ), - &body, - ) - .await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } - - #[instrument(skip(client, access_token, body))] - pub async fn send_state( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - state_key: Option, - body: T, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let mut path = format!( - "/_matrix/client/v3/rooms/{room_id}/state/{event_type}", - room_id = room_id, - event_type = body.event_type(), - ); - - if let Some(state_key) = state_key { - path.push_str(&format!("/{state_key}", state_key = state_key)) - } - - let resp = tmp.put_json(path, &body).await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } - - #[instrument(skip(client, access_token, body))] - pub async fn send_redaction( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_id: &EventId, - txn_id: OwnedTransactionId, - body: SendRedactionBody, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .put_json( - format!( - "/_matrix/client/v3/rooms/{room_id}/redact/{event_id}/{txn_id}", - room_id = room_id, - event_id = event_id, - txn_id = txn_id, - ), - &body, - ) - .await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } -} diff --git a/crates/matrix/src/client-backup/membership.rs.bk.bk b/crates/matrix/src/client-backup/membership.rs.bk.bk deleted file mode 100644 index 86c3778..0000000 --- a/crates/matrix/src/client-backup/membership.rs.bk.bk +++ /dev/null @@ -1,5 +0,0 @@ -pub mod ban; -pub mod join; -pub mod kick; -pub mod leave; -pub mod unban; diff --git a/crates/matrix/src/client-backup/mod.rs.bk.bk b/crates/matrix/src/client-backup/mod.rs.bk.bk deleted file mode 100644 index cc296e6..0000000 --- a/crates/matrix/src/client-backup/mod.rs.bk.bk +++ /dev/null @@ -1,10 +0,0 @@ -//! This module is the root of the client-server API. -//! -//! reference: https://spec.matrix.org/unstable/client-server-api - -pub mod rooms; -pub mod session; -pub mod membership; -pub mod uiaa; -pub mod sync; -pub mod account; diff --git a/crates/matrix/src/client-backup/mxc.rs.bk.bk.bk b/crates/matrix/src/client-backup/mxc.rs.bk.bk.bk deleted file mode 100644 index 7dc669a..0000000 --- a/crates/matrix/src/client-backup/mxc.rs.bk.bk.bk +++ /dev/null @@ -1,184 +0,0 @@ -use std::str::FromStr; - -use anyhow::Result; -use mime::Mime; -use ruma_common::{MxcUri, OwnedMxcUri}; -use serde::{de, Deserialize, Deserializer, Serialize}; -use tracing::instrument; - -use chrono::{serde::ts_microseconds_option, DateTime, Utc}; - -use crate::error::MatrixError; - -fn parse_mime_opt<'de, D>(d: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - Option::<&str>::deserialize(d)? - .map(::from_str) - .transpose() - .map_err(de::Error::custom) -} - -#[derive(Debug, Serialize)] -pub struct GetPreviewUrlQuery { - pub url: url::Url, - pub ts: DateTime, -} - -#[derive(Debug, Deserialize)] -pub struct CreateMxcUriResponse { - pub content_uri: String, - - #[serde(with = "ts_microseconds_option")] - pub unused_expires_at: Option>, -} - -#[derive(Debug, Deserialize)] -pub struct GetPreviewUrlResponse { - #[serde(rename = "matrix:image_size")] - pub image_size: Option, - - #[serde(rename = "og:description")] - pub description: Option, - - #[serde(rename = "og:image")] - pub image: Option, - - #[serde(rename = "og:image:height")] - pub height: Option, - - #[serde(rename = "og:image:width")] - pub width: Option, - - #[serde(rename = "og:image:type", deserialize_with = "parse_mime_opt")] - pub kind: Option, - - #[serde(rename = "og:title")] - pub title: Option, -} - -#[derive(Debug, Deserialize)] -pub struct GetConfigResponse { - #[serde(rename = "m.upload.size")] - pub upload_size: Option, -} - -#[derive(Debug, Serialize)] -pub enum ResizeMethod { - Crop, - Scale, -} - -pub struct MxcService; - -#[derive(Debug, Deserialize)] -pub struct MxcError { - #[serde(flatten)] - pub inner: MatrixError, - - pub retry_after_ms: u64, -} - -impl MxcService { - /// Creates a new `MxcUri`, independently of the content being uploaded - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#post_matrixmediav1create - #[instrument(skip(client, access_token))] - pub async fn create( - client: &crate::http::Client, - access_token: impl Into, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp.post("/_matrix/media/v1/create").await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.inner.error)) - } - - /// Retrieve the configuration of the content repository - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#get_matrixmediav3config - #[instrument(skip(client, access_token))] - pub async fn get_config( - client: &crate::http::Client, - access_token: impl Into, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp.get("/_matrix/media/v3/config").await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.inner.error)) - } - - /// Retrieve a URL to download content from the content repository, - /// optionally replacing the name of the file. - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#get_matrixmediav3downloadservernamemediaid - #[instrument(skip(client, access_token))] - pub async fn get_download_url( - client: &crate::http::Client, - access_token: impl Into, - mxc_uri: &MxcUri, - mut base_url: url::Url, - file_name: Option, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let (server_name, media_id) = mxc_uri.parts().unwrap(); - - let mut path = format!( - "/_matrix/media/v3/download/{server_name}/{media_id}", - server_name = server_name, - media_id = media_id, - ); - - if let Some(file_name) = file_name { - path.push_str(&format!("/{file_name}", file_name = file_name)) - } - - base_url.set_path(&path); - - Ok(base_url) - } - - /// - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#get_matrixmediav3preview_url - #[instrument(skip(client, access_token))] - pub async fn get_preview( - client: &crate::http::Client, - access_token: impl Into, - query: GetPreviewUrlQuery, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get_query("/_matrix/media/v3/preview_url".to_string(), &query) - .await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.inner.error)) - } -} diff --git a/crates/matrix/src/client-backup/rooms.rs.bk.bk b/crates/matrix/src/client-backup/rooms.rs.bk.bk deleted file mode 100644 index 2d4cdf8..0000000 --- a/crates/matrix/src/client-backup/rooms.rs.bk.bk +++ /dev/null @@ -1,6 +0,0 @@ -//! This module contains handlers to interact with rooms. -//! -//! reference: https://spec.matrix.org/unstable/client-server-api/#rooms - -pub mod create; -pub mod forget; diff --git a/crates/matrix/src/client-backup/session.rs.bk.bk.bk b/crates/matrix/src/client-backup/session.rs.bk.bk.bk deleted file mode 100644 index 6bceee3..0000000 --- a/crates/matrix/src/client-backup/session.rs.bk.bk.bk +++ /dev/null @@ -1,2 +0,0 @@ -pub mod create; -pub mod invalidate; diff --git a/crates/matrix/src/client-backup/sync.rs.bk.bk b/crates/matrix/src/client-backup/sync.rs.bk.bk deleted file mode 100644 index 906f9cf..0000000 --- a/crates/matrix/src/client-backup/sync.rs.bk.bk +++ /dev/null @@ -1,564 +0,0 @@ -//! This module contains handlers for getting and synchronizing events. -//! -//! reference: https://github.com/matrix-org/matrix-spec-proposals/pull/3575 - -use std::{collections::BTreeMap, time::Duration}; - -use ruma_common::{ - api::{request, response, Metadata}, - metadata, - serde::{deserialize_cow_str, duration::opt_ms, Raw}, - DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, -}; -use ruma_events::{ - receipt::SyncReceiptEvent, typing::SyncTypingEvent, AnyGlobalAccountDataEvent, - AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, AnySyncTimelineEvent, - AnyToDeviceEvent, StateEventType, TimelineEventType, -}; -use serde::{self, de::Error as _, Deserialize, Serialize}; - -const METADATA: Metadata = metadata! { - method: POST, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_matrix/client/unstable/org.matrix.msc3575/sync", - // 1.4 => "/_matrix/client/v4/sync", - } -}; - -#[request(error = crate::Error)] -#[derive(Default)] -pub struct Request { - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub pos: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub delta_token: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub conn_id: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub txn_id: Option, - - #[serde(with = "opt_ms", default, skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub timeout: Option, - - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub lists: BTreeMap, - - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub room_subscriptions: BTreeMap, - - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub unsubscribe_rooms: Vec, - - #[serde(default, skip_serializing_if = "ExtensionsConfig::is_empty")] - pub extensions: ExtensionsConfig, -} - -#[response(error = crate::Error)] -pub struct Response { - #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")] - pub initial: bool, - - #[serde(skip_serializing_if = "Option::is_none")] - pub txn_id: Option, - - pub pos: String, - - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub lists: BTreeMap, - - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub rooms: BTreeMap, - - #[serde(default, skip_serializing_if = "Extensions::is_empty")] - pub extensions: Extensions, - - pub delta_token: Option, -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct UnreadNotificationsCount { - #[serde(skip_serializing_if = "Option::is_none")] - pub highlight_count: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub notification_count: Option, -} - -impl UnreadNotificationsCount { - pub fn is_empty(&self) -> bool { - self.highlight_count.is_none() && self.notification_count.is_none() - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct DeviceLists { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub changed: Vec, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub left: Vec, -} - -impl DeviceLists { - pub fn is_empty(&self) -> bool { - self.changed.is_empty() && self.left.is_empty() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct SyncRequestListFilters { - #[serde(skip_serializing_if = "Option::is_none")] - pub is_dm: Option, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub spaces: Vec, - - #[serde(skip_serializing_if = "Option::is_none")] - pub is_encrypted: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub is_invite: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub is_tombstoned: Option, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub room_types: Vec, - - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub not_room_types: Vec, - - #[serde(skip_serializing_if = "Option::is_none")] - pub room_name_like: Option, - - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub tags: Vec, - - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub not_tags: Vec, - - #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")] - pub extensions: BTreeMap, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct SyncRequestList { - #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")] - pub slow_get_all_rooms: bool, - - pub ranges: Vec<(usize, usize)>, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub sort: Vec, - - #[serde(flatten)] - pub room_details: RoomDetailsConfig, - - #[serde(skip_serializing_if = "Option::is_none")] - pub include_old_rooms: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub filters: Option, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub bump_event_types: Vec, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct RoomDetailsConfig { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub required_state: Vec<(StateEventType, String)>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub timeline_limit: Option, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct IncludeOldRooms { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub required_state: Vec<(StateEventType, String)>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub timeline_limit: Option, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct RoomSubscription { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub required_state: Vec<(StateEventType, String)>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub timeline_limit: Option, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "UPPERCASE")] -pub enum SlidingOp { - Sync, - - Insert, - - Delete, - - Invalidate, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct SyncList { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub ops: Vec, - - pub count: usize, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct SyncOp { - pub op: SlidingOp, - - pub range: Option<(usize, usize)>, - - pub index: Option, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub room_ids: Vec, - - pub room_id: Option, -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct SlidingSyncRoom { - #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub avatar: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub initial: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub is_dm: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub invite_state: Option>>, - - #[serde( - flatten, - default, - skip_serializing_if = "UnreadNotificationsCount::is_empty" - )] - pub unread_notifications: UnreadNotificationsCount, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub timeline: Vec>, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub required_state: Vec>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub prev_batch: Option, - - #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")] - pub limited: bool, - - #[serde(skip_serializing_if = "Option::is_none")] - pub joined_count: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub invited_count: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub num_live: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub timestamp: Option, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct ExtensionsConfig { - #[serde(default, skip_serializing_if = "ToDeviceConfig::is_empty")] - pub to_device: ToDeviceConfig, - - #[serde(default, skip_serializing_if = "E2EEConfig::is_empty")] - pub e2ee: E2EEConfig, - - #[serde(default, skip_serializing_if = "AccountDataConfig::is_empty")] - pub account_data: AccountDataConfig, - - #[serde(default, skip_serializing_if = "ReceiptsConfig::is_empty")] - pub receipts: ReceiptsConfig, - - #[serde(default, skip_serializing_if = "TypingConfig::is_empty")] - pub typing: TypingConfig, - - #[serde(flatten)] - other: BTreeMap, -} - -impl ExtensionsConfig { - pub fn is_empty(&self) -> bool { - self.to_device.is_empty() - && self.e2ee.is_empty() - && self.account_data.is_empty() - && self.receipts.is_empty() - && self.typing.is_empty() - && self.other.is_empty() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct Extensions { - #[serde(skip_serializing_if = "Option::is_none")] - pub to_device: Option, - - #[serde(default, skip_serializing_if = "E2EE::is_empty")] - pub e2ee: E2EE, - - #[serde(default, skip_serializing_if = "AccountData::is_empty")] - pub account_data: AccountData, - - #[serde(default, skip_serializing_if = "Receipts::is_empty")] - pub receipts: Receipts, - - #[serde(default, skip_serializing_if = "Typing::is_empty")] - pub typing: Typing, -} - -impl Extensions { - pub fn is_empty(&self) -> bool { - self.to_device.is_none() - && self.e2ee.is_empty() - && self.account_data.is_empty() - && self.receipts.is_empty() - && self.typing.is_empty() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct ToDeviceConfig { - #[serde(skip_serializing_if = "Option::is_none")] - pub enabled: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub since: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub lists: Option>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub rooms: Option>, -} - -impl ToDeviceConfig { - pub fn is_empty(&self) -> bool { - self.enabled.is_none() && self.limit.is_none() && self.since.is_none() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct ToDevice { - pub next_batch: String, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct E2EEConfig { - #[serde(skip_serializing_if = "Option::is_none")] - pub enabled: Option, -} - -impl E2EEConfig { - pub fn is_empty(&self) -> bool { - self.enabled.is_none() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct E2EE { - #[serde(default, skip_serializing_if = "DeviceLists::is_empty")] - pub device_lists: DeviceLists, - - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub device_one_time_keys_count: BTreeMap, - - #[serde(skip_serializing_if = "Option::is_none")] - pub device_unused_fallback_key_types: Option>, -} - -impl E2EE { - pub fn is_empty(&self) -> bool { - self.device_lists.is_empty() - && self.device_one_time_keys_count.is_empty() - && self.device_unused_fallback_key_types.is_none() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct AccountDataConfig { - #[serde(skip_serializing_if = "Option::is_none")] - pub enabled: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub lists: Option>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub rooms: Option>, -} - -impl AccountDataConfig { - pub fn is_empty(&self) -> bool { - self.enabled.is_none() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct AccountData { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub global: Vec>, - - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub rooms: BTreeMap>>, -} - -impl AccountData { - pub fn is_empty(&self) -> bool { - self.global.is_empty() && self.rooms.is_empty() - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum RoomReceiptConfig { - AllSubscribed, - - Room(OwnedRoomId), -} - -impl Serialize for RoomReceiptConfig { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - RoomReceiptConfig::AllSubscribed => serializer.serialize_str("*"), - RoomReceiptConfig::Room(r) => r.serialize(serializer), - } - } -} - -impl<'de> Deserialize<'de> for RoomReceiptConfig { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - match deserialize_cow_str(deserializer)?.as_ref() { - "*" => Ok(RoomReceiptConfig::AllSubscribed), - other => Ok(RoomReceiptConfig::Room( - RoomId::parse(other).map_err(D::Error::custom)?.to_owned(), - )), - } - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct ReceiptsConfig { - #[serde(skip_serializing_if = "Option::is_none")] - pub enabled: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub lists: Option>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub rooms: Option>, -} - -impl ReceiptsConfig { - pub fn is_empty(&self) -> bool { - self.enabled.is_none() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct Receipts { - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub rooms: BTreeMap>, -} - -impl Receipts { - pub fn is_empty(&self) -> bool { - self.rooms.is_empty() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct TypingConfig { - #[serde(skip_serializing_if = "Option::is_none")] - pub enabled: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub lists: Option>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub rooms: Option>, -} - -impl TypingConfig { - pub fn is_empty(&self) -> bool { - self.enabled.is_none() - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct Typing { - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub rooms: BTreeMap>, -} - -impl Typing { - pub fn is_empty(&self) -> bool { - self.rooms.is_empty() - } -} - -#[cfg(test)] -mod tests { - use ruma_common::owned_room_id; - - use super::RoomReceiptConfig; - - #[test] - fn serialize_room_receipt_config() { - let entry = RoomReceiptConfig::AllSubscribed; - assert_eq!(serde_json::to_string(&entry).unwrap().as_str(), r#""*""#); - - let entry = RoomReceiptConfig::Room(owned_room_id!("!n8f893n9:example.com")); - assert_eq!( - serde_json::to_string(&entry).unwrap().as_str(), - r#""!n8f893n9:example.com""# - ); - } - - #[test] - fn deserialize_room_receipt_config() { - assert_eq!( - serde_json::from_str::(r#""*""#).unwrap(), - RoomReceiptConfig::AllSubscribed - ); - - assert_eq!( - serde_json::from_str::(r#""!n8f893n9:example.com""#).unwrap(), - RoomReceiptConfig::Room(owned_room_id!("!n8f893n9:example.com")) - ); - } -} diff --git a/crates/matrix/src/client-backup/uiaa.rs.bk.bk b/crates/matrix/src/client-backup/uiaa.rs.bk.bk deleted file mode 100644 index c417d30..0000000 --- a/crates/matrix/src/client-backup/uiaa.rs.bk.bk +++ /dev/null @@ -1,164 +0,0 @@ -//! Module for [User-Interactive Authentication API][uiaa] types. -//! -//! [uiaa]: https://spec.matrix.org/latest/client-server-api/#user-interactive-authentication-api - -use ruma_common::{thirdparty::Medium, OwnedSessionId, OwnedUserId, UserId}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct UiaaResponse { - pub flows: Vec, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub completed: Vec, - - pub params: Box, - - #[serde(skip_serializing_if = "Option::is_none")] - pub session: Option, - // #[serde(flatten, skip_serializing_if = "Option::is_none")] - // pub auth_error: Option, -} - -/// Ordered list of stages required to complete authentication. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct AuthFlow { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub stages: Vec, -} - -impl AuthFlow { - pub fn new(stages: Vec) -> Self { - Self { stages } - } -} - -/// Information for one authentication stage. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[non_exhaustive] -pub enum AuthType { - /// Password-based authentication (`m.login.password`). - #[serde(rename = "m.login.password")] - Password, - - /// Google ReCaptcha 2.0 authentication (`m.login.recaptcha`). - #[serde(rename = "m.login.recaptcha")] - ReCaptcha, - - /// Email-based authentication (`m.login.email.identity`). - #[serde(rename = "m.login.email.identity")] - EmailIdentity, - - /// Phone number-based authentication (`m.login.msisdn`). - #[serde(rename = "m.login.msisdn")] - Msisdn, - - /// SSO-based authentication (`m.login.sso`). - #[serde(rename = "m.login.sso")] - Sso, - - /// Dummy authentication (`m.login.dummy`). - #[serde(rename = "m.login.dummy")] - Dummy, - - /// Registration token-based authentication (`m.login.registration_token`). - #[serde(rename = "m.login.registration_token")] - RegistrationToken, -} - -#[derive(Clone, Debug, Serialize)] -#[non_exhaustive] -#[serde(untagged)] -pub enum AuthData { - // Password-based authentication (`m.login.password`). - Password(Password), - - // Google ReCaptcha 2.0 authentication (`m.login.recaptcha`). - // ReCaptcha(ReCaptcha), - - // Email-based authentication (`m.login.email.identity`). - // EmailIdentity(EmailIdentity), - - // Phone number-based authentication (`m.login.msisdn`). - // Msisdn(Msisdn), - - // Dummy authentication (`m.login.dummy`). - Dummy(Dummy), - // Registration token-based authentication (`m.login.registration_token`). - // RegistrationToken(RegistrationToken), - - // Fallback acknowledgement. - // FallbackAcknowledgement(FallbackAcknowledgement), -} - -impl AuthData { - fn kind(&self) -> AuthType { - match self { - AuthData::Password(_) => AuthType::Password, - AuthData::Dummy(_) => AuthType::Dummy, - } - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(tag = "type", rename = "m.login.dummy")] -pub struct Dummy {} - -impl Dummy { - pub fn new() -> Self { - Self::default() - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(tag = "type", rename = "m.login.password")] -pub struct Password { - identifier: UserIdentifier, - password: String, -} - -impl Password { - pub fn new>(user_id: impl Into, password: S) -> Self { - let user: &UserId = &user_id.into(); - - Self { - identifier: UserIdentifier::User { - user: user.localpart().to_owned(), - }, - password: password.into(), - } - } -} - -#[derive(Clone, Debug, Serialize)] -pub struct UiaaRequest { - session: Option, - - kind: AuthType, - - #[serde(flatten)] - data: AuthData, -} - -impl UiaaRequest { - pub fn new(data: AuthData, session: Option) -> Self { - Self { - session, - kind: data.kind(), - data, - } - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum UserIdentifier { - #[serde(rename = "m.id.user")] - User { user: String }, - - #[serde(rename = "m.id.thirdparty")] - ThirdParty { medium: Medium, address: String }, - - #[serde(rename = "m.id.phone")] - Phone { country: String, phone: String }, -} diff --git a/crates/matrix/src/client.rs b/crates/matrix/src/client.rs deleted file mode 100644 index b9f8de2..0000000 --- a/crates/matrix/src/client.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! This module is the root of the client-server API. -//! -//! reference: https://spec.matrix.org/unstable/client-server-api - -pub mod account; -pub mod login; -pub mod logout; -pub mod profile; -pub mod register; -pub mod uiaa; diff --git a/crates/matrix/src/client/account.rs b/crates/matrix/src/client/account.rs deleted file mode 100644 index 7f46308..0000000 --- a/crates/matrix/src/client/account.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod password; -pub mod whoami; diff --git a/crates/matrix/src/client/account/password.rs b/crates/matrix/src/client/account/password.rs deleted file mode 100644 index 7ab5fc0..0000000 --- a/crates/matrix/src/client/account/password.rs +++ /dev/null @@ -1,55 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedUserId, -}; -use serde::Serialize; - -use crate::client::uiaa::{self, Auth, AuthData}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: POST, - rate_limited: true, - authentication: AccessToken, - history: { - unstable => "/_matrix/client/v3/account/password", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - pub auth: Auth, - - pub logout_devices: bool, - - pub new_password: String, -} - -impl Request { - pub fn new(new_password: String) -> Self { - Self { - auth: Auth::new(AuthData::Dummy(uiaa::Dummy {}), None), - logout_devices: false, - new_password, - } - } - - pub fn with_password( - mut self, - user_id: OwnedUserId, - password: String, - // auth_session: Option>, - ) -> Self { - self.auth = Auth::new( - AuthData::Password(uiaa::Password::new(user_id, password)), - // auth_session.map(Into::into), - None, - ); - - self - } -} - -#[response(error = crate::Error)] -#[derive(Serialize)] -pub struct Response {} diff --git a/crates/matrix/src/client/account/whoami.rs b/crates/matrix/src/client/account/whoami.rs deleted file mode 100644 index c2f1f73..0000000 --- a/crates/matrix/src/client/account/whoami.rs +++ /dev/null @@ -1,32 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedDeviceId, OwnedUserId, -}; -use serde::Serialize; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: true, - authentication: AccessToken, - history: { - unstable => "/_matrix/client/v3/account/whoami", - } -}; - -#[request(error = crate::Error)] -pub struct Request {} - -impl Request { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self {} - } -} - -#[response(error = crate::Error)] -#[derive(Serialize)] -pub struct Response { - pub device_id: OwnedDeviceId, - pub user_id: OwnedUserId, -} diff --git a/crates/matrix/src/client/login.rs b/crates/matrix/src/client/login.rs deleted file mode 100644 index 153f0d6..0000000 --- a/crates/matrix/src/client/login.rs +++ /dev/null @@ -1,134 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedDeviceId, OwnedMxcUri, OwnedUserId, -}; -use serde::{Deserialize, Serialize}; - -use crate::client::uiaa::UserIdentifier; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: POST, - rate_limited: true, - authentication: None, - history: { - unstable => "/_matrix/client/v3/login", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[serde(flatten, rename = "type")] - pub kind: LoginType, - - #[serde(skip_serializing_if = "Option::is_none")] - pub identifier: Option, - - #[serde( - rename = "initial_device_display_name", - skip_serializing_if = "String::is_empty" - )] - pub device_name: String, - - #[serde(skip_serializing_if = "Option::is_none")] - pub refresh_token: Option, -} - -impl Request { - pub fn new( - kind: LoginType, - identifier: Option, - device_name: String, - refresh_token: Option, - ) -> Self { - Self { - kind, - identifier, - device_name, - refresh_token, - } - } -} - -#[response(error = crate::Error)] -#[derive(Deserialize, Serialize)] -pub struct Response { - pub access_token: String, - - pub device_id: OwnedDeviceId, - - #[serde(skip_serializing_if = "Option::is_none")] - pub expires_in_ms: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub refresh_token: Option, - - pub user_id: OwnedUserId, - - #[serde(skip_serializing_if = "Option::is_none")] - pub well_known: Option, -} - -// impl Response { -// pub fn new>( -// access_token: S, -// refresh_token: Option, -// expires_in_ms: Option, -// user_id: impl Into, -// device_id: impl Into, -// well_known: Option, -// ) -> Self { -// Self { -// access_token: access_token.into(), -// refresh_token: refresh_token.map(Into::into), -// expires_in_ms, -// device_id: device_id.into(), -// user_id: user_id.into(), -// well_known, -// } -// } -// } - -#[derive(Clone, Debug, Serialize)] -pub struct IdentityProvider { - pub id: String, - - #[serde(skip_serializing_if = "String::is_empty")] - pub name: String, - - #[serde(skip_serializing_if = "Option::is_none")] - pub icon: Option, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(tag = "type")] -pub enum LoginType { - #[serde(rename = "m.login.password")] - Password { password: String }, - - #[serde(rename = "m.login.token")] - Token { token: String }, - - #[serde(rename = "m.login.sso")] - Sso { - #[serde(skip_serializing_if = "<[_]>::is_empty")] - identity_providers: Vec, - }, - - #[serde(rename = "m.login.application_service")] - ApplicationService, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct BaseUrl { - pub base_url: url::Url, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct WellKnown { - #[serde(rename = "m.homeserver")] - pub homeserver: BaseUrl, - - #[serde(rename = "m.identity_server")] - pub identity_server: BaseUrl, -} diff --git a/crates/matrix/src/client/logout.rs b/crates/matrix/src/client/logout.rs deleted file mode 100644 index 2422763..0000000 --- a/crates/matrix/src/client/logout.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod all; -pub mod root; diff --git a/crates/matrix/src/client/logout/all.rs b/crates/matrix/src/client/logout/all.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/matrix/src/client/logout/all.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/matrix/src/client/logout/root.rs b/crates/matrix/src/client/logout/root.rs deleted file mode 100644 index 10d0cbb..0000000 --- a/crates/matrix/src/client/logout/root.rs +++ /dev/null @@ -1,29 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, -}; -use serde::{Deserialize, Serialize}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: POST, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_matrix/client/v3/logout", - } -}; - -#[request(error = crate::Error)] -pub struct Request {} - -#[allow(clippy::new_without_default)] -impl Request { - pub fn new() -> Self { - Self {} - } -} - -#[response(error = crate::Error)] -#[derive(Deserialize, Serialize)] -pub struct Response {} diff --git a/crates/matrix/src/client/profile.rs b/crates/matrix/src/client/profile.rs deleted file mode 100644 index 58428f4..0000000 --- a/crates/matrix/src/client/profile.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod avatar_url; -pub mod display_name; diff --git a/crates/matrix/src/client/profile/avatar_url.rs b/crates/matrix/src/client/profile/avatar_url.rs deleted file mode 100644 index 0e93baa..0000000 --- a/crates/matrix/src/client/profile/avatar_url.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod get; -pub mod update; diff --git a/crates/matrix/src/client/profile/avatar_url/get.rs b/crates/matrix/src/client/profile/avatar_url/get.rs deleted file mode 100644 index 1d18ad6..0000000 --- a/crates/matrix/src/client/profile/avatar_url/get.rs +++ /dev/null @@ -1,31 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedMxcUri, OwnedUserId, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: None, - history: { - unstable => "/_matrix/client/v3/profile/:user_id/avatar_url", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub user_id: OwnedUserId, -} - -impl Request { - pub fn new(user_id: OwnedUserId) -> Self { - Self { user_id } - } -} - -#[response(error = crate::Error)] -pub struct Response { - pub avatar_url: OwnedMxcUri, -} diff --git a/crates/matrix/src/client/profile/avatar_url/update.rs b/crates/matrix/src/client/profile/avatar_url/update.rs deleted file mode 100644 index d291379..0000000 --- a/crates/matrix/src/client/profile/avatar_url/update.rs +++ /dev/null @@ -1,36 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedMxcUri, OwnedUserId, -}; -use serde::Serialize; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: PUT, - rate_limited: true, - authentication: AccessToken, - history: { - unstable => "/_matrix/client/v3/profile/:user_id/avatar_url", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub user_id: OwnedUserId, - - pub avatar_url: OwnedMxcUri, -} - -impl Request { - pub fn new(user_id: OwnedUserId, avatar_url: OwnedMxcUri) -> Self { - Self { - user_id, - avatar_url, - } - } -} - -#[response(error = crate::Error)] -#[derive(Serialize)] -pub struct Response {} diff --git a/crates/matrix/src/client/profile/display_name.rs b/crates/matrix/src/client/profile/display_name.rs deleted file mode 100644 index 0e93baa..0000000 --- a/crates/matrix/src/client/profile/display_name.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod get; -pub mod update; diff --git a/crates/matrix/src/client/profile/display_name/get.rs b/crates/matrix/src/client/profile/display_name/get.rs deleted file mode 100644 index 7ce9d9a..0000000 --- a/crates/matrix/src/client/profile/display_name/get.rs +++ /dev/null @@ -1,32 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedUserId, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: false, - authentication: None, - history: { - unstable => "/_matrix/client/v3/profile/:user_id/displayname", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub user_id: OwnedUserId, -} - -impl Request { - pub fn new(user_id: OwnedUserId) -> Self { - Self { user_id } - } -} - -#[response(error = crate::Error)] -pub struct Response { - #[serde(rename = "displayname")] - pub display_name: String, -} diff --git a/crates/matrix/src/client/profile/display_name/update.rs b/crates/matrix/src/client/profile/display_name/update.rs deleted file mode 100644 index 5cac54f..0000000 --- a/crates/matrix/src/client/profile/display_name/update.rs +++ /dev/null @@ -1,35 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedUserId, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: PUT, - rate_limited: true, - authentication: AccessToken, - history: { - unstable => "/_matrix/client/v3/profile/:user_id/displayname", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(path)] - pub user_id: OwnedUserId, - - #[serde(rename = "displayname")] - pub display_name: String, -} - -impl Request { - pub fn new(user_id: OwnedUserId, display_name: String) -> Self { - Self { - user_id, - display_name, - } - } -} - -#[response(error = crate::Error)] -pub struct Response {} diff --git a/crates/matrix/src/client/register.rs b/crates/matrix/src/client/register.rs deleted file mode 100644 index b518083..0000000 --- a/crates/matrix/src/client/register.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod available; -pub mod root; -pub mod token; diff --git a/crates/matrix/src/client/register/available.rs b/crates/matrix/src/client/register/available.rs deleted file mode 100644 index f5ecc37..0000000 --- a/crates/matrix/src/client/register/available.rs +++ /dev/null @@ -1,33 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, -}; -use serde::Serialize; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: true, - authentication: None, - history: { - unstable => "/_matrix/client/v3/register/available", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(query)] - pub username: String, -} - -impl Request { - pub fn new(username: String) -> Self { - Self { username } - } -} - -#[response(error = crate::Error)] -#[derive(Serialize)] -pub struct Response { - pub available: bool, -} diff --git a/crates/matrix/src/client/register/root.rs b/crates/matrix/src/client/register/root.rs deleted file mode 100644 index 860021d..0000000 --- a/crates/matrix/src/client/register/root.rs +++ /dev/null @@ -1,76 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, OwnedDeviceId, OwnedUserId, -}; -use serde::{Deserialize, Serialize}; - -use crate::client::uiaa::Auth; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: POST, - rate_limited: true, - authentication: None, - history: { - unstable => "/_matrix/client/v3/register", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - pub username: String, - - pub password: String, - - #[serde( - rename = "initial_device_display_name", - skip_serializing_if = "Option::is_none" - )] - pub device_name: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub refresh_token: Option, - - /// Note that this information is not used to define how the registered user - /// should be authenticated, but is instead used to authenticate the - /// register call itself. It should be left empty, or omitted, unless an - /// earlier call returned an response with status code 401. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option, -} - -impl Request { - pub fn new( - username: String, - password: String, - device_name: Option, - refresh_token: Option, - auth: Option, - ) -> Self { - Self { - username, - password, - device_name, - refresh_token, - auth, - } - } -} - -#[response(error = crate::Error)] -#[derive(Deserialize, Serialize)] -pub struct Response { - #[serde(default)] - pub access_token: Option, - - #[serde(default)] - pub device_id: Option, - - #[serde(default)] - pub expires_in_ms: Option, - - #[serde(default)] - pub refresh_token: Option, - - pub user_id: OwnedUserId, -} diff --git a/crates/matrix/src/client/register/token.rs b/crates/matrix/src/client/register/token.rs deleted file mode 100644 index 113a424..0000000 --- a/crates/matrix/src/client/register/token.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod validity; diff --git a/crates/matrix/src/client/register/token/validity.rs b/crates/matrix/src/client/register/token/validity.rs deleted file mode 100644 index 98c9fa9..0000000 --- a/crates/matrix/src/client/register/token/validity.rs +++ /dev/null @@ -1,31 +0,0 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: GET, - rate_limited: true, - authentication: None, - history: { - unstable => "/_matrix/client/v1/register/m.login.registration_token/validity", - } -}; - -#[request(error = crate::Error)] -pub struct Request { - #[ruma_api(query)] - pub token: String, -} - -impl Request { - pub fn new(token: String) -> Self { - Self { token } - } -} - -#[response(error = crate::Error)] -pub struct Response { - pub valid: bool, -} diff --git a/crates/matrix/src/client/uiaa.rs b/crates/matrix/src/client/uiaa.rs deleted file mode 100644 index a49a517..0000000 --- a/crates/matrix/src/client/uiaa.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! Module for [User-Interactive Authentication API][uiaa] types. -//! -//! [uiaa]: https://spec.matrix.org/latest/client-server-api/#user-interactive-authentication-api - -use ruma_common::{thirdparty::Medium, OwnedSessionId, OwnedUserId, UserId}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct UiaaResponse { - pub flows: Vec, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub completed: Vec, - - pub params: Box, - - #[serde(skip_serializing_if = "Option::is_none")] - pub session: Option, - // #[serde(flatten, skip_serializing_if = "Option::is_none")] - // pub auth_error: Option, -} - -/// Ordered list of stages required to complete authentication. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct AuthFlow { - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub stages: Vec, -} - -impl AuthFlow { - pub fn new(stages: Vec) -> Self { - Self { stages } - } -} - -/// Information for one authentication stage. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[non_exhaustive] -pub enum AuthType { - /// Password-based authentication (`m.login.password`). - #[serde(rename = "m.login.password")] - Password, - - /// Google ReCaptcha 2.0 authentication (`m.login.recaptcha`). - #[serde(rename = "m.login.recaptcha")] - ReCaptcha, - - /// Email-based authentication (`m.login.email.identity`). - #[serde(rename = "m.login.email.identity")] - EmailIdentity, - - /// Phone number-based authentication (`m.login.msisdn`). - #[serde(rename = "m.login.msisdn")] - Msisdn, - - /// SSO-based authentication (`m.login.sso`). - #[serde(rename = "m.login.sso")] - Sso, - - /// Dummy authentication (`m.login.dummy`). - #[serde(rename = "m.login.dummy")] - Dummy, - - /// Registration token-based authentication (`m.login.registration_token`). - #[serde(rename = "m.login.registration_token")] - RegistrationToken, -} - -#[derive(Clone, Debug, Serialize)] -#[non_exhaustive] -#[serde(untagged)] -pub enum AuthData { - // Password-based authentication (`m.login.password`). - Password(Password), - - // Google ReCaptcha 2.0 authentication (`m.login.recaptcha`). - // ReCaptcha(ReCaptcha), - - // Email-based authentication (`m.login.email.identity`). - // EmailIdentity(EmailIdentity), - - // Phone number-based authentication (`m.login.msisdn`). - // Msisdn(Msisdn), - - // Dummy authentication (`m.login.dummy`). - Dummy(Dummy), - // Registration token-based authentication (`m.login.registration_token`). - // RegistrationToken(RegistrationToken), - - // Fallback acknowledgement. - // FallbackAcknowledgement(FallbackAcknowledgement), -} - -impl AuthData { - fn kind(&self) -> AuthType { - match self { - AuthData::Password(_) => AuthType::Password, - AuthData::Dummy(_) => AuthType::Dummy, - } - } -} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(tag = "type", rename = "m.login.dummy")] -pub struct Dummy {} - -impl Dummy { - pub fn new() -> Self { - Self::default() - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(tag = "type", rename = "m.login.password")] -pub struct Password { - identifier: UserIdentifier, - password: String, -} - -impl Password { - pub fn new>(user_id: impl Into, password: S) -> Self { - let user: &UserId = &user_id.into(); - - Self { - identifier: UserIdentifier::User { - user: user.localpart().to_owned(), - }, - password: password.into(), - } - } -} - -#[derive(Clone, Debug, Serialize)] -pub struct Auth { - session: Option, - - kind: AuthType, - - #[serde(flatten)] - data: AuthData, -} - -impl Auth { - pub fn new(data: AuthData, session: Option) -> Self { - Self { - session, - kind: data.kind(), - data, - } - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum UserIdentifier { - #[serde(rename = "m.id.user")] - User { user: String }, - - #[serde(rename = "m.id.thirdparty")] - ThirdParty { medium: Medium, address: String }, - - #[serde(rename = "m.id.phone")] - Phone { country: String, phone: String }, -} diff --git a/crates/matrix/src/lib.rs b/crates/matrix/src/lib.rs deleted file mode 100644 index 6a7e5be..0000000 --- a/crates/matrix/src/lib.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! This library deals with forwarding Matrix requests to the server. -//! Comments have been used sparingly as the specification contains all the -//! technical details. - -//! We rely on `ruma` to abstract away the boilerplate introduced by HTTP -//! requests, without sacrificing flexibility by defining our own request and -//! response types. -//! -//! reference: https://docs.ruma.io/ruma_common/api/index.html - -pub mod admin; -pub mod client; - -use async_trait::async_trait; -use bytes::{Bytes, BytesMut}; -use ruma_client::HttpClient; - -pub use ruma_client; -pub use ruma_common; -pub use ruma_events; -pub use ruma_identifiers_validation; - -pub type Error = ruma_common::api::error::MatrixError; -pub type HandleError = ruma_client::Error; - -#[derive(Default, Debug)] -pub struct Client { - inner: reqwest::Client, -} - -#[async_trait] -impl HttpClient for Client { - type RequestBody = BytesMut; - type ResponseBody = Bytes; - type Error = reqwest::Error; - - async fn send_http_request( - &self, - req: http::Request, - ) -> Result, reqwest::Error> { - let req = req.map(|body| body.freeze()).try_into()?; - let mut res = self.inner.execute(req).await?; - - let mut http_builder = http::Response::builder() - .status(res.status()) - .version(res.version()); - std::mem::swap( - http_builder - .headers_mut() - .expect("http::response::Builder to be usable"), - res.headers_mut(), - ); - - Ok(http_builder - .body(res.bytes().await?) - .expect("http::Response construction to work")) - } -} diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml deleted file mode 100644 index c44e461..0000000 --- a/crates/router/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "router" -version = "0.0.0" -edition = "2021" -publish = false - -[[bin]] -name = "commune-server" -path = "src/main.rs" - -[lib] -name = "router" -path = "src/lib.rs" - -[dependencies] -axum = { workspace = true, features = ["tokio", "macros"] } -axum-extra = { workspace = true, features = ["typed-header"] } -anyhow = { workspace = true } -http = { workspace = true } -email_address = { workspace = true } -# openssl = { workspace = true, features = ["vendored"] } -# openssl-sys = { workspace = true, features = ["vendored"] } -serde = { workspace = true, features = ["derive"] } -tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] } -tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter"] } -url = { workspace = true, features = ["serde"] } - -# Local Dependencies -core = { path = "../core" } -matrix = { path = "../matrix" } -figment = { workspace = true, features = ["toml", "env"] } diff --git a/crates/router/src/api.rs b/crates/router/src/api.rs deleted file mode 100644 index 9756a11..0000000 --- a/crates/router/src/api.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! This module is the root of the client-server API. -//! -//! reference: https://spec.matrix.org/unstable/client-server-api - -pub mod account; -pub mod relative; -// pub mod session; diff --git a/crates/router/src/api/account.rs b/crates/router/src/api/account.rs deleted file mode 100644 index 080944c..0000000 --- a/crates/router/src/api/account.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod avatar; -pub mod display_name; -pub mod email; -pub mod password; -pub mod whoami; diff --git a/crates/router/src/api/account/avatar.rs b/crates/router/src/api/account/avatar.rs deleted file mode 100644 index 2dbb812..0000000 --- a/crates/router/src/api/account/avatar.rs +++ /dev/null @@ -1,31 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{ - headers::{authorization::Bearer, Authorization}, - TypedHeader, -}; -use matrix::ruma_common::OwnedMxcUri; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct Payload { - pub mxc_uri: OwnedMxcUri, -} - -pub async fn handler( - TypedHeader(access_token): TypedHeader>, - Json(payload): Json, -) -> Response { - use commune::profile::avatar::update::service; - - match service(access_token.token(), payload.mxc_uri).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to update avatar"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/account/display_name.rs b/crates/router/src/api/account/display_name.rs deleted file mode 100644 index 7467469..0000000 --- a/crates/router/src/api/account/display_name.rs +++ /dev/null @@ -1,30 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{ - headers::{authorization::Bearer, Authorization}, - TypedHeader, -}; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct Payload { - pub display_name: String, -} - -pub async fn handler( - TypedHeader(access_token): TypedHeader>, - Json(payload): Json, -) -> Response { - use commune::profile::avatar::update::service; - - match service(access_token.token(), payload.display_name).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to update display name"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/account/email.rs b/crates/router/src/api/account/email.rs deleted file mode 100644 index 6fb66d1..0000000 --- a/crates/router/src/api/account/email.rs +++ /dev/null @@ -1,19 +0,0 @@ -use axum::{ - extract::Path, - response::{IntoResponse, Response}, - Json, -}; -use email_address::EmailAddress; - -pub async fn handler(Path(email): Path) -> Response { - use commune::account::email::service; - - match service(email).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to handle email verification"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/account/password.rs b/crates/router/src/api/account/password.rs deleted file mode 100644 index 37d28e4..0000000 --- a/crates/router/src/api/account/password.rs +++ /dev/null @@ -1,40 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{ - headers::{authorization::Bearer, Authorization}, - TypedHeader, -}; -use commune::util::secret::Secret; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct Payload { - username: String, - password: Secret, - new_password: Secret, -} - -pub async fn handler( - TypedHeader(access_token): TypedHeader>, - Json(payload): Json, -) -> Response { - use commune::account::password::service; - - match service( - access_token.token(), - payload.username, - payload.password, - payload.new_password, - ) - .await - { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to reset password"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/account/whoami.rs b/crates/router/src/api/account/whoami.rs deleted file mode 100644 index c5a1f91..0000000 --- a/crates/router/src/api/account/whoami.rs +++ /dev/null @@ -1,21 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{ - headers::{authorization::Bearer, Authorization}, - TypedHeader, -}; - -pub async fn handler(TypedHeader(access_token): TypedHeader>) -> Response { - use commune::account::whoami::service; - - match service(access_token.token()).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to associate access token with user"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/relative.rs b/crates/router/src/api/relative.rs deleted file mode 100644 index a5a6a98..0000000 --- a/crates/router/src/api/relative.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod available; -pub mod login; -pub mod logout; -pub mod register; diff --git a/crates/router/src/api/relative/available.rs b/crates/router/src/api/relative/available.rs deleted file mode 100644 index e37cbb0..0000000 --- a/crates/router/src/api/relative/available.rs +++ /dev/null @@ -1,18 +0,0 @@ -use axum::{ - extract::Path, - response::{IntoResponse, Response}, - Json, -}; - -pub async fn handler(Path(username): Path) -> Response { - use commune::account::username::service; - - match service(username).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to check username availability"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/relative/login.rs b/crates/router/src/api/relative/login.rs deleted file mode 100644 index d8501f8..0000000 --- a/crates/router/src/api/relative/login.rs +++ /dev/null @@ -1,25 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use commune::util::secret::Secret; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Deserialize, Serialize)] -pub struct Payload { - pub username: String, - pub password: Secret, -} - -pub async fn handler(Json(payload): Json) -> Response { - use commune::account::login::service; - - match service(&payload.username, &payload.password).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to login user"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/relative/logout.rs b/crates/router/src/api/relative/logout.rs deleted file mode 100644 index 2a492da..0000000 --- a/crates/router/src/api/relative/logout.rs +++ /dev/null @@ -1,21 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{ - headers::{authorization::Bearer, Authorization}, - TypedHeader, -}; - -pub async fn handler(TypedHeader(access_token): TypedHeader>) -> Response { - use commune::account::logout::service; - - match service(access_token.token()).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to logout user"); - - e.into_response() - } - } -} diff --git a/crates/router/src/api/relative/register.rs b/crates/router/src/api/relative/register.rs deleted file mode 100644 index e9a5323..0000000 --- a/crates/router/src/api/relative/register.rs +++ /dev/null @@ -1,25 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use commune::util::secret::Secret; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Payload { - pub username: String, - pub password: Secret, -} - -pub async fn handler(Json(payload): Json) -> Response { - use commune::account::register::service; - - match service(payload.username, payload.password).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to create account"); - - e.into_response() - } - } -} diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs deleted file mode 100644 index a3785e1..0000000 --- a/crates/router/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::net::SocketAddr; - -use axum::{ - routing::{get, post, put}, - Router, -}; -use tokio::net::TcpListener; - -pub mod api; - -pub async fn routes() -> Router { - let router = Router::new() - .route("/register", post(api::relative::register::handler)) - .route( - "/register/available/:username", - get(api::relative::available::handler), - ) - .route("/login", post(api::relative::login::handler)) - .route("/logout", post(api::relative::logout::handler)) - .nest( - "/account", - Router::new() - .route("/whoami", get(api::account::whoami::handler)) - .route("/password", put(api::account::password::handler)) - .route("/display_name", put(api::account::display_name::handler)) - .route("/avatar", put(api::account::avatar::handler)), - ); - - Router::new().nest("/_commune/client/r0", router) -} - -pub async fn serve(public_loopback: bool, port: u16) -> anyhow::Result<()> { - let host = match public_loopback { - true => [0, 0, 0, 0], - false => [127, 0, 0, 1], - }; - - let addr = SocketAddr::from((host, port)); - let tcp_listener = TcpListener::bind(addr).await?; - - tracing::info!("Listening on {}", addr); - - let router = routes().await; - - axum::serve(tcp_listener, router.into_make_service()) - .await - .map_err(Into::into) -} diff --git a/crates/router/src/main.rs b/crates/router/src/main.rs deleted file mode 100644 index 7991edc..0000000 --- a/crates/router/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -use anyhow::Result; - -#[tokio::main] -async fn main() -> Result<()> { - tracing_subscriber::fmt::init(); - - commune::init().await; - let config = &commune::commune().config; - - router::serve(config.public_loopback, config.port.unwrap()).await?; - - Ok(()) -} diff --git a/crates/router/src/router/api/mod.rs b/crates/router/src/router/api/mod.rs deleted file mode 100644 index bd8c77a..0000000 --- a/crates/router/src/router/api/mod.rs +++ /dev/null @@ -1,90 +0,0 @@ -pub mod v1; - -use axum::{response::IntoResponse, Json, Router}; -use http::StatusCode; -use serde::{Deserialize, Serialize}; - -use commune::error::HttpStatusCode; - -pub struct Api; - -impl Api { - pub fn routes() -> Router { - Router::new().nest("/api", Router::new().nest("/v1", v1::V1::routes())) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct ApiError { - pub message: String, - pub code: String, - #[serde(skip)] - pub status: StatusCode, -} - -impl ApiError { - pub fn new(message: String, code: String, status: StatusCode) -> Self { - Self { - message, - code, - status, - } - } - - pub fn unauthorized() -> Self { - Self::new( - "You must be authenticated to access this resource".to_string(), - "UNAUTHORIZED".to_string(), - StatusCode::UNAUTHORIZED, - ) - } - - pub fn internal_server_error() -> Self { - Self::new( - "Internal server error".to_string(), - "INTERNAL_SERVER_ERROR".to_string(), - StatusCode::INTERNAL_SERVER_ERROR, - ) - } -} - -impl From for ApiError { - fn from(err: commune::error::Error) -> Self { - Self { - message: err.to_string(), - code: err.error_code().to_string(), - status: err.status_code(), - } - } -} - -/// Any `anyhow::Error` can be converted into an `ApiError`. -/// -/// Caveat is that given that anyhow error is generic (w/o context), the -/// error status is 500. -/// -/// Perhaps in the future, a more specific error type can be used, like with -/// `thiserror`. -impl From for ApiError { - fn from(err: anyhow::Error) -> Self { - Self { - message: err.to_string(), - code: "UNKNOWN_ERROR".to_string(), - status: StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl IntoResponse for ApiError { - fn into_response(self) -> axum::response::Response { - if let Ok(status) = axum::http::StatusCode::from_u16(self.status.as_u16()) { - let mut response = Json(self).into_response(); - - *response.status_mut() = status; - return response; - } - - tracing::error!(status=%self.status, "Failed to convert status code to http::StatusCode"); - ApiError::internal_server_error().into_response() - } -} diff --git a/crates/router/src/router/api/v1/account/email.rs b/crates/router/src/router/api/v1/account/email.rs deleted file mode 100644 index 004d93a..0000000 --- a/crates/router/src/router/api/v1/account/email.rs +++ /dev/null @@ -1,34 +0,0 @@ -use axum::{ - extract::Path, - http::StatusCode, - response::{IntoResponse, Response}, - Extension, Json, -}; -use serde::{Deserialize, Serialize}; -use tracing::instrument; - -use crate::{router::api::ApiError, services::SharedServices}; - -#[instrument(skip(services))] -pub async fn handler( - Extension(services): Extension, - Path(email): Path, -) -> Response { - match services.commune.account.is_email_available(&email).await { - Ok(available) => { - let mut response = Json(AccountEmailExistsResponse { available }).into_response(); - - *response.status_mut() = StatusCode::OK; - response - } - Err(err) => { - tracing::warn!(?err, ?email, "Failed to find email"); - ApiError::from(err).into_response() - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AccountEmailExistsResponse { - pub available: bool, -} diff --git a/crates/router/src/router/api/v1/account/login.rs b/crates/router/src/router/api/v1/account/login.rs deleted file mode 100644 index 0257bcd..0000000 --- a/crates/router/src/router/api/v1/account/login.rs +++ /dev/null @@ -1,95 +0,0 @@ -use axum::{ - http::StatusCode, - response::{IntoResponse, Response}, - Extension, Json, -}; -use commune::Error; -use serde::{Deserialize, Serialize}; -use tracing::instrument; - -use commune::auth::service::LoginCredentials; - -use crate::{router::api::ApiError, services::SharedServices}; - -use super::root::{AccountMatrixCredentials, AccountSpace}; - -#[instrument(skip(services))] -pub async fn get(Extension(services): Extension) -> Response { - match services.commune.auth.get_login_flows().await { - Ok(flows) => Json(flows).into_response(), - Err(err) => { - tracing::warn!(?err, "Failed to retrieve login flows"); - ApiError::from(err).into_response() - } - } -} - -#[instrument(skip(services, payload))] -pub async fn post( - Extension(services): Extension, - Json(payload): Json, -) -> Response { - let login_credentials = LoginCredentials::from(payload); - - let Ok(tokens) = services.commune.auth.login(login_credentials).await else { - tracing::warn!("Failed to authenticate user"); - return ApiError::from(Error::Auth( - commune::auth::error::AuthErrorCode::InvalidCredentials, - )) - .into_response(); - }; - - match services.commune.account.whoami(&tokens.access_token).await { - Ok(account) => { - let mut response = Json(AccountLoginResponse { - access_token: tokens.access_token.to_string(), - credentials: AccountMatrixCredentials { - username: account.username, - display_name: account.display_name, - avatar_url: account.avatar_url, - access_token: tokens.access_token.to_string(), - matrix_access_token: tokens.access_token.to_string(), - matrix_user_id: account.user_id.to_string(), - matrix_device_id: String::new(), - user_space_id: String::new(), - email: account.email, - age: account.age, - admin: account.admin, - verified: account.verified, - }, - ..Default::default() - }) - .into_response(); - - *response.status_mut() = StatusCode::OK; - response - } - Err(err) => { - tracing::warn!(?err, "Failed to authenticate user"); - ApiError::from(err).into_response() - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AccountLoginPayload { - pub username: String, - pub password: String, -} - -impl From for LoginCredentials { - fn from(payload: AccountLoginPayload) -> Self { - Self { - username: payload.username, - password: payload.password.into(), - } - } -} - -#[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct AccountLoginResponse { - pub access_token: String, - pub credentials: AccountMatrixCredentials, - pub rooms: Vec, - pub spaces: Vec, -} diff --git a/crates/router/src/router/api/v1/account/mod.rs b/crates/router/src/router/api/v1/account/mod.rs deleted file mode 100644 index a1b74f6..0000000 --- a/crates/router/src/router/api/v1/account/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub mod email; -pub mod login; -pub mod root; -pub mod session; -pub mod verify_code; -pub mod verify_code_email; - -use axum::{ - middleware, - routing::{get, post}, - Router, -}; - -use crate::router::middleware::auth; - -pub struct Account; - -impl Account { - pub fn routes() -> Router { - Router::new() - .route("/session", get(session::handler)) - .route_layer(middleware::from_fn(auth)) - .route("/", post(root::handler)) - .route("/login", get(login::get)) - .route("/login", post(login::post)) - .route("/login/sso/redirect", get(login::get)) - .route("/email/:email", get(email::handler)) - .nest( - "/verify", - Router::new() - .route("/code", post(verify_code::handler)) - .route("/code/email", post(verify_code_email::handler)), - ) - } -} diff --git a/crates/router/src/router/api/v1/account/root.rs b/crates/router/src/router/api/v1/account/root.rs deleted file mode 100644 index 6358ded..0000000 --- a/crates/router/src/router/api/v1/account/root.rs +++ /dev/null @@ -1,128 +0,0 @@ -use axum::{ - http::StatusCode, - response::{IntoResponse, Response}, - Extension, Json, -}; - -use serde::{Deserialize, Serialize}; -use tracing::instrument; - -use commune::account::{model::Account, service::CreateAccountDto}; -use url::Url; -use uuid::Uuid; - -use crate::{router::api::ApiError, services::SharedServices}; - -#[instrument(skip(services, payload))] -pub async fn handler( - Extension(services): Extension, - Json(payload): Json, -) -> Response { - let dto = CreateAccountDto::from(payload); - - match services.commune.account.register(dto).await { - Ok(account) => { - let access_token = services - .commune - .account - .issue_user_token(&account.user_id) - .await - .unwrap(); - let payload = AccountRegisterResponse { - access_token: access_token.to_string(), - created: true, - credentials: AccountMatrixCredentials { - username: account.username, - display_name: account.display_name, - avatar_url: account.avatar_url, - access_token: access_token.to_string(), - matrix_access_token: access_token.to_string(), - matrix_user_id: account.user_id.to_string(), - matrix_device_id: "".to_string(), - user_space_id: "".to_string(), - email: account.email, - age: account.age, - admin: account.admin, - verified: account.verified, - }, - ..Default::default() - }; - - let mut response = Json(payload).into_response(); - - *response.status_mut() = StatusCode::CREATED; - response - } - Err(err) => { - tracing::warn!(?err, "Failed to register user"); - ApiError::from(err).into_response() - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AccountRegisterPayload { - pub username: String, - pub password: String, - pub email: String, - pub session: Uuid, - pub code: String, -} - -impl From for CreateAccountDto { - fn from(payload: AccountRegisterPayload) -> Self { - Self { - username: payload.username, - password: payload.password.into(), - email: payload.email, - session: payload.session, - code: payload.code.into(), - } - } -} - -#[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct AccountSpace { - pub room_id: String, - pub alias: String, - pub name: String, - pub topic: Option, - pub avatar: Option, - pub header: Option, - pub is_profile: bool, - pub is_default: bool, - pub is_owner: bool, -} - -#[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct AccountMatrixCredentials { - pub username: String, - pub display_name: String, - pub avatar_url: Option, - pub access_token: String, - pub matrix_access_token: String, - pub matrix_user_id: String, - pub matrix_device_id: String, - pub user_space_id: String, - pub email: String, - pub age: i64, - pub admin: bool, - pub verified: bool, -} - -#[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct AccountRegisterResponse { - pub access_token: String, - pub created: bool, - pub credentials: AccountMatrixCredentials, - pub rooms: Vec, - pub spaces: Vec, -} - -impl From for AccountRegisterResponse { - fn from(_: Account) -> Self { - Self { - ..Default::default() - } - } -} diff --git a/crates/router/src/router/api/v1/account/session.rs b/crates/router/src/router/api/v1/account/session.rs deleted file mode 100644 index f17c737..0000000 --- a/crates/router/src/router/api/v1/account/session.rs +++ /dev/null @@ -1,48 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Extension, Json, -}; -use serde::{Deserialize, Serialize}; -use tracing::instrument; - -use commune::account::model::Account; - -use crate::router::middleware::AccessToken; - -use super::root::{AccountMatrixCredentials, AccountSpace}; - -#[instrument(skip(account))] -pub async fn handler( - Extension(account): Extension, - Extension(access_token): Extension, -) -> Response { - let response = Json(AccountSessionResponse { - credentials: AccountMatrixCredentials { - username: account.username, - display_name: account.display_name, - avatar_url: account.avatar_url, - access_token: access_token.to_string(), - matrix_access_token: access_token.to_string(), - matrix_user_id: account.user_id.to_string(), - matrix_device_id: String::new(), - user_space_id: String::new(), - email: account.email, - age: account.age, - admin: account.admin, - verified: account.verified, - }, - rooms: vec![], - spaces: vec![], - valid: true, - }); - - response.into_response() -} - -#[derive(Debug, Clone, Default, Deserialize, Serialize)] -pub struct AccountSessionResponse { - pub credentials: AccountMatrixCredentials, - pub rooms: Vec, - pub spaces: Vec, - pub valid: bool, -} diff --git a/crates/router/src/router/api/v1/account/verify_code.rs b/crates/router/src/router/api/v1/account/verify_code.rs deleted file mode 100644 index 0f135e0..0000000 --- a/crates/router/src/router/api/v1/account/verify_code.rs +++ /dev/null @@ -1,74 +0,0 @@ -use axum::{ - http::StatusCode, - response::{IntoResponse, Response}, - Extension, Json, -}; -use commune::{account::error::AccountErrorCode, Error}; -use serde::{Deserialize, Serialize}; -use tracing::instrument; -use uuid::Uuid; - -use commune::account::service::SendCodeDto; - -use crate::{router::api::ApiError, services::SharedServices}; - -#[instrument(skip(services, payload))] -pub async fn handler( - Extension(services): Extension, - Json(payload): Json, -) -> Response { - let dto = SendCodeDto::from(payload); - - match services - .commune - .account - .is_email_available(&dto.email) - .await - { - Ok(available) => { - if !available { - let email_taken_error = AccountErrorCode::EmailTaken(dto.email); - let error = Error::User(email_taken_error); - - return ApiError::from(error).into_response(); - } - } - Err(err) => { - tracing::warn!(?err, ?dto, "Failed to verify email availability"); - return ApiError::from(err).into_response(); - } - } - - match services.commune.account.send_code(dto).await { - Ok(_) => { - let mut response = Json(VerifyCodeResponse { sent: true }).into_response(); - - *response.status_mut() = StatusCode::OK; - response - } - Err(err) => { - tracing::warn!(?err, "Failed to register user"); - ApiError::from(err).into_response() - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AccountVerifyCodePayload { - pub email: String, - pub session: Uuid, -} - -impl From for SendCodeDto { - fn from(payload: AccountVerifyCodePayload) -> Self { - Self { - email: payload.email, - session: payload.session, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct VerifyCodeResponse { - pub sent: bool, -} diff --git a/crates/router/src/router/api/v1/account/verify_code_email.rs b/crates/router/src/router/api/v1/account/verify_code_email.rs deleted file mode 100644 index a5b8205..0000000 --- a/crates/router/src/router/api/v1/account/verify_code_email.rs +++ /dev/null @@ -1,76 +0,0 @@ -use axum::{ - http::StatusCode, - response::{IntoResponse, Response}, - Extension, Json, -}; -use commune::{account::error::AccountErrorCode, util::secret::Secret, Error}; -use serde::{Deserialize, Serialize}; -use tracing::instrument; -use uuid::Uuid; - -use commune::account::service::VerifyCodeDto; - -use crate::{router::api::ApiError, services::SharedServices}; - -#[instrument(skip(services, payload))] -pub async fn handler( - Extension(services): Extension, - Json(payload): Json, -) -> Response { - let dto = VerifyCodeDto::from(payload); - - match services - .commune - .account - .is_email_available(&dto.email) - .await - { - Ok(available) => { - if !available { - let email_taken_error = AccountErrorCode::EmailTaken(dto.email); - let error = Error::User(email_taken_error); - - return ApiError::from(error).into_response(); - } - } - Err(err) => { - tracing::warn!(?err, ?dto, "Failed to verify email availability"); - return ApiError::from(err).into_response(); - } - } - - match services.commune.account.verify_code(dto).await { - Ok(valid) => { - let mut response = Json(VerifyCodeEmailResponse { valid }).into_response(); - - *response.status_mut() = StatusCode::OK; - response - } - Err(err) => { - tracing::warn!(?err, "Failed to register user"); - ApiError::from(err).into_response() - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct AccountVerifyCodeEmailPayload { - pub email: String, - pub session: Uuid, - pub code: Secret, -} - -impl From for VerifyCodeDto { - fn from(payload: AccountVerifyCodeEmailPayload) -> Self { - Self { - email: payload.email, - session: payload.session, - code: payload.code, - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct VerifyCodeEmailResponse { - pub valid: bool, -} diff --git a/crates/router/src/router/api/v1/mod.rs b/crates/router/src/router/api/v1/mod.rs deleted file mode 100644 index 360418c..0000000 --- a/crates/router/src/router/api/v1/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod account; - -use axum::Router; - -pub struct V1; - -impl V1 { - pub fn routes() -> Router { - Router::new().nest("/account", account::Account::routes()) - } -} diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml deleted file mode 100644 index 382d607..0000000 --- a/crates/test/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "test" -version = "0.0.0" -edition = "2021" -publish = false - -[lib] -name = "test" -path = "src/lib.rs" - -[dependencies] -# Workspace Dependencies -anyhow = { workspace = true } -axum = { workspace = true, features = ["tokio"] } -reqwest = { workspace = true, features = ["json"] } -serde = { workspace = true } -tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] } -thiserror = { workspace = true } -url = { workspace = true } -rand = { workspace = true } -tracing = { workspace = true } -tracing-subscriber = { workspace = true } - -# Local Dependencies -core = { path = "../core" } -matrix = { path = "../matrix" } -router = { path = "../router" } diff --git a/crates/test/fixtures/synapse/homeserver.yaml b/crates/test/fixtures/synapse/homeserver.yaml deleted file mode 100644 index f6450c8..0000000 --- a/crates/test/fixtures/synapse/homeserver.yaml +++ /dev/null @@ -1,84 +0,0 @@ -# Configuration file for Synapse. -# -# This is a YAML file: see [1] for a quick introduction. Note in particular -# that *indentation is important*: all the elements of a list or dictionary -# should have the same indentation. -# -# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html -# -# For more information on how to configure Synapse, including a complete accounting of -# each option, go to docs/usage/configuration/config_documentation.md or -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html -server_name: "matrix.localhost" -pid_file: /data/homeserver.pid - -listeners: - - port: 8008 - tls: false - type: http - x_forwarded: true - bind_addresses: ['::', '0.0.0.0'] - resources: - - names: [client, federation] - compress: false -database: - name: psycopg2 - txn_limit: 10000 - allow_unsafe_locale: true - args: - user: synapse_user - password: secretpassword - database: synapse - host: localhost - port: 5432 - cp_min: 5 - cp_max: 10 -log_config: "/data/matrix.localhost.log.config" -media_store_path: /data/media_store -registration_shared_secret: "m@;wYOUOh0f:CH5XA65sJB1^q01~DmIriOysRImot,OR_vzN&B" -report_stats: true -macaroon_secret_key: "XND.g+P_7wz.Yx:i6js.Eh;=jG*#uWBIe;X2OoX78^E,LVJ;8c" -form_secret: "pS7pR@AFJD~BtUAqH^ku5Kenz1X^Hol0E_+xhwvohOrkx;sMoO" -signing_key_path: "/data/matrix.localhost.signing.key" -trusted_key_servers: - - server_name: "matrix.org" - -rc_message: - per_second: 1000 - burst_count: 1000 -rc_registration: - per_second: 1000 - burst_count: 1000 -rc_login: - address: - per_second: 1000 - burst_count: 1000 - account: - per_second: 1000 - burst_count: 1000 - failed_attempts: - per_second: 1000 - burst_count: 1000 -rc_admin_redaction: - per_second: 1000 - burst_count: 1000 -rc_joins: - local: - per_second: 1000 - burst_count: 1000 - remote: - per_second: 1000 - burst_count: 1000 -rc_3pid_validation: - per_second: 1000 - burst_count: 1000 -rc_invites: - per_room: - per_second: 1000 - burst_count: 1000 - per_user: - per_second: 1000 - burst_count: 1000 - -enable_registration: true -enable_registration_without_verification: true diff --git a/crates/test/fixtures/synapse/matrix.localhost.log.config b/crates/test/fixtures/synapse/matrix.localhost.log.config deleted file mode 100644 index 1fda721..0000000 --- a/crates/test/fixtures/synapse/matrix.localhost.log.config +++ /dev/null @@ -1,35 +0,0 @@ -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - file: - class: logging.handlers.TimedRotatingFileHandler - formatter: precise - filename: /data/homeserver.log - when: midnight - backupCount: 3 - encoding: utf8 - - buffer: - class: synapse.logging.handlers.PeriodicallyFlushingMemoryHandler - target: file - capacity: 10 - - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: INFO -root: - level: INFO - handlers: [console] - - -disable_existing_loggers: false diff --git a/crates/test/fixtures/synapse/matrix.localhost.signing.key b/crates/test/fixtures/synapse/matrix.localhost.signing.key deleted file mode 100644 index 090b449..0000000 --- a/crates/test/fixtures/synapse/matrix.localhost.signing.key +++ /dev/null @@ -1 +0,0 @@ -ed25519 a_VKUD FXu3HoEKJdiMh1e+3dW8kO/P8ldSdNzdV+/vg9wdowE diff --git a/crates/test/src/api.rs b/crates/test/src/api.rs deleted file mode 100644 index 4230649..0000000 --- a/crates/test/src/api.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! This module is the root of the client-server API. -//! -//! reference: https://spec.matrix.org/unstable/client-server-api - -// pub mod account; -pub mod relative; -// pub mod session; diff --git a/crates/test/src/api/account.rs b/crates/test/src/api/account.rs deleted file mode 100644 index 080944c..0000000 --- a/crates/test/src/api/account.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod avatar; -pub mod display_name; -pub mod email; -pub mod password; -pub mod whoami; diff --git a/crates/test/src/api/account/avatar.rs b/crates/test/src/api/account/avatar.rs deleted file mode 100644 index 3c55ff8..0000000 --- a/crates/test/src/api/account/avatar.rs +++ /dev/null @@ -1,33 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{headers::{authorization::Bearer, Authorization}, TypedHeader}; -use matrix::ruma_common::OwnedMxcUri; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct Payload { - pub mxc_uri: OwnedMxcUri, -} - -pub async fn handler( - TypedHeader(access_token): TypedHeader>, - Json(payload): Json, -) -> Response { - use commune::profile::avatar::update::service; - - match service( - access_token.token(), - payload.mxc_uri, - ) - .await - { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to update avatar"); - - e.into_response() - } - } -} diff --git a/crates/test/src/api/account/display_name.rs b/crates/test/src/api/account/display_name.rs deleted file mode 100644 index 0f9b321..0000000 --- a/crates/test/src/api/account/display_name.rs +++ /dev/null @@ -1,27 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{headers::{authorization::Bearer, Authorization}, TypedHeader}; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct Payload { - pub display_name: String, -} - -pub async fn handler( - TypedHeader(access_token): TypedHeader>, - Json(payload): Json, -) -> Response { - use commune::profile::avatar::update::service; - - match service(access_token.token(), payload.display_name).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to update display name"); - - e.into_response() - } - } -} diff --git a/crates/test/src/api/account/email.rs b/crates/test/src/api/account/email.rs deleted file mode 100644 index 6fb66d1..0000000 --- a/crates/test/src/api/account/email.rs +++ /dev/null @@ -1,19 +0,0 @@ -use axum::{ - extract::Path, - response::{IntoResponse, Response}, - Json, -}; -use email_address::EmailAddress; - -pub async fn handler(Path(email): Path) -> Response { - use commune::account::email::service; - - match service(email).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to handle email verification"); - - e.into_response() - } - } -} diff --git a/crates/test/src/api/account/password.rs b/crates/test/src/api/account/password.rs deleted file mode 100644 index b3ea639..0000000 --- a/crates/test/src/api/account/password.rs +++ /dev/null @@ -1,37 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{headers::{authorization::Bearer, Authorization}, TypedHeader}; -use commune::util::secret::Secret; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct Payload { - username: String, - password: Secret, - new_password: Secret, -} - -pub async fn handler( - TypedHeader(access_token): TypedHeader>, - Json(payload): Json, -) -> Response { - use commune::account::password::service; - - match service( - access_token.token(), - payload.username, - payload.password, - payload.new_password, - ) - .await - { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to reset password"); - - e.into_response() - } - } -} diff --git a/crates/test/src/api/account/whoami.rs b/crates/test/src/api/account/whoami.rs deleted file mode 100644 index c5a1f91..0000000 --- a/crates/test/src/api/account/whoami.rs +++ /dev/null @@ -1,21 +0,0 @@ -use axum::{ - response::{IntoResponse, Response}, - Json, -}; -use axum_extra::{ - headers::{authorization::Bearer, Authorization}, - TypedHeader, -}; - -pub async fn handler(TypedHeader(access_token): TypedHeader>) -> Response { - use commune::account::whoami::service; - - match service(access_token.token()).await { - Ok(resp) => Json(resp).into_response(), - Err(e) => { - tracing::warn!(?e, "failed to associate access token with user"); - - e.into_response() - } - } -} diff --git a/crates/test/src/api/relative.rs b/crates/test/src/api/relative.rs deleted file mode 100644 index a5a6a98..0000000 --- a/crates/test/src/api/relative.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod available; -pub mod login; -pub mod logout; -pub mod register; diff --git a/crates/test/src/api/relative/available.rs b/crates/test/src/api/relative/available.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/test/src/api/relative/available.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/test/src/api/relative/login.rs b/crates/test/src/api/relative/login.rs deleted file mode 100644 index cdd28de..0000000 --- a/crates/test/src/api/relative/login.rs +++ /dev/null @@ -1,34 +0,0 @@ -use commune::util::secret::Secret; -use matrix::client::login::*; -use router::api::relative::login; - -use crate::{api::relative::register, env::Env}; - -pub async fn login(client: &Env) -> Result { - let register_resp = register::register(&client).await.unwrap(); - - tracing::info!(?register_resp); - - let resp = client - .post("/_commune/client/r0/login") - .json(&login::Payload { - username: register_resp.user_id.into(), - password: Secret::new("verysecure"), - }) - .send() - .await - .unwrap(); - - resp.json::().await -} - -#[tokio::test] -async fn login_test() { - let client = Env::new().await; - - let resp = login(&client).await.unwrap(); - - tracing::info!(?resp); - - assert!(!resp.access_token.is_empty()); -} diff --git a/crates/test/src/api/relative/logout.rs b/crates/test/src/api/relative/logout.rs deleted file mode 100644 index eaf583a..0000000 --- a/crates/test/src/api/relative/logout.rs +++ /dev/null @@ -1,30 +0,0 @@ -use matrix::client::logout::root::*; - -use crate::{api::relative::login, env::Env}; - -pub async fn logout(client: &Env) -> Result { - let login_resp = login::login(&client).await.unwrap(); - - tracing::info!(?login_resp); - - let resp = client - .post("/_commune/client/r0/logout") - .header( - reqwest::header::AUTHORIZATION, - format!("Bearer {}", &login_resp.access_token), - ) - .send() - .await - .unwrap(); - - resp.json::().await -} - -#[tokio::test] -async fn logout_test() { - let client = Env::new().await; - - let resp = logout(&client).await.unwrap(); - - tracing::info!(?resp); -} diff --git a/crates/test/src/api/relative/register.rs b/crates/test/src/api/relative/register.rs deleted file mode 100644 index fbe5b88..0000000 --- a/crates/test/src/api/relative/register.rs +++ /dev/null @@ -1,42 +0,0 @@ -use commune::util::secret::Secret; -use rand::seq::IteratorRandom; - -use matrix::client::register::root::*; -use router::api::relative::register; - -use crate::env::Env; - -pub async fn register(client: &Env) -> Result { - let allowed = ('0'..='9') - .chain('a'..='z') - .chain(['-', '.', '=', '_', '/', '+']); - let username = allowed - .choose_multiple(&mut rand::thread_rng(), 8) - .into_iter() - .collect(); - - tracing::info!(?username); - - let resp = client - .post("/_commune/client/r0/register") - .json(®ister::Payload { - username, - password: Secret::new("verysecure"), - }) - .send() - .await - .unwrap(); - - resp.json::().await -} - -#[tokio::test] -async fn register_test() { - let client = Env::new().await; - - let resp = register(&client).await.unwrap(); - - tracing::info!(?resp); - - assert!(resp.access_token.is_some() && resp.access_token.map(|at| !at.is_empty()).unwrap()); -} diff --git a/crates/test/src/env.rs b/crates/test/src/env.rs deleted file mode 100644 index 3407572..0000000 --- a/crates/test/src/env.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::net::SocketAddr; - -pub(crate) struct Env { - pub client: reqwest::Client, - pub loopback: SocketAddr, -} - -impl Env { - pub(crate) async fn new() -> Self { - let _ = tracing_subscriber::fmt().try_init(); - - commune::init().await; - - let loopback = SocketAddr::from(( - match commune::commune().config.public_loopback { - true => [0, 0, 0, 0], - false => [127, 0, 0, 1], - }, - 5357, - )); - - tokio::spawn(async move { - tracing::info!("starting development server on {:?}", loopback); - - router::serve(commune::commune().config.public_loopback, 5357) - .await - .expect("failed to bind to address"); - }); - - let client = reqwest::Client::builder() - .redirect(reqwest::redirect::Policy::none()) - .build() - .unwrap(); - - if let Err(e) = client - .get(commune::commune().config.matrix.host.to_string() + "/_matrix/client/versions") - .send() - .await - { - tracing::error!( - "could not connect to Matrix: {e}\n is the testing environment running?" - ); - - std::process::exit(1); - } - - Self { client, loopback } - } - - fn path(&self, path: &str) -> String { - format!("http://{}{}", self.loopback, path) - } - - #[allow(dead_code)] - pub(crate) fn get(&self, url: &str) -> reqwest::RequestBuilder { - tracing::info!("GET {}", self.path(url)); - - self.client.get(self.path(url)) - } - - pub(crate) fn post(&self, url: &str) -> reqwest::RequestBuilder { - tracing::info!("POST {}", self.path(url)); - - self.client.post(self.path(url)) - } -} diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs deleted file mode 100644 index c84f5e1..0000000 --- a/crates/test/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -// #[cfg(test)] -// mod commune; - -// #[cfg(test)] -// mod tools; - -// #[cfg(test)] -// mod matrix; - -// #[cfg(test)] -// mod server; - -#[cfg(test)] -mod api; - -#[cfg(test)] -mod env; diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 18a4f56..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,45 +0,0 @@ -version: '3' - -services: - mailcrab: - image: marlonb/mailcrab:latest - ports: - - '1025:1025' - networks: [default] - - redis: - image: 'redis/redis-stack' - ports: - - '6379:6379' - - '8001:8001' - volumes: - - redis-db:/data - - synapse-db: - image: 'postgres:16' - ports: - - '5432:5432' - volumes: - - synapse-db:/var/lib/postgresql/data - env_file: - - .env - restart: always - - synapse: - image: 'ghcr.io/element-hq/synapse:v1.100.0' - user: "${DOCKER_USER}" - ports: - - '8008:8008' - - '8448:8448' - volumes: - - ./docker/synapse:/data - env_file: - - .env - restart: always - network_mode: 'host' - depends_on: - - synapse-db - -volumes: - redis-db: - synapse-db: diff --git a/docs/diagrams/diagram.excalidraw b/docs/diagrams/diagram.excalidraw deleted file mode 100644 index eaa5793..0000000 --- a/docs/diagrams/diagram.excalidraw +++ /dev/null @@ -1,475 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": [ - { - "id": "RYL6z1yNI4ryhDrjxemyL", - "type": "rectangle", - "x": 364, - "y": 272, - "width": 130, - "height": 119, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1352799417, - "version": 94, - "versionNonce": 297889623, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "NwNRXHacaPiTQKk-sS3iv" - }, - { - "id": "jR76iIplo4TVvNKZBH6vT", - "type": "arrow" - } - ], - "updated": 1700421911263, - "link": null, - "locked": false - }, - { - "id": "NwNRXHacaPiTQKk-sS3iv", - "type": "text", - "x": 393.84375, - "y": 319.5, - "width": 70.3125, - "height": 24, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 683172535, - "version": 80, - "versionNonce": 581421529, - "isDeleted": false, - "boundElements": null, - "updated": 1700421905506, - "link": null, - "locked": false, - "text": "Client", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "RYL6z1yNI4ryhDrjxemyL", - "originalText": "Client", - "lineHeight": 1.2 - }, - { - "type": "rectangle", - "version": 207, - "versionNonce": 145190007, - "isDeleted": false, - "id": "igO3c0IESW8ZSDcllatP-", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 582, - "y": 269.5, - "strokeColor": "#2f9e44", - "backgroundColor": "#b2f2bb", - "width": 130, - "height": 119, - "seed": 2026968473, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "E1cQEf95N_pZWvm755-z4" - }, - { - "id": "jR76iIplo4TVvNKZBH6vT", - "type": "arrow" - }, - { - "id": "I--VThaGzeWEpMH77IyuH", - "type": "arrow" - } - ], - "updated": 1700421911263, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 206, - "versionNonce": 648226489, - "isDeleted": false, - "id": "E1cQEf95N_pZWvm755-z4", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 605.984375, - "y": 305, - "strokeColor": "#2f9e44", - "backgroundColor": "#ffec99", - "width": 82.03125, - "height": 48, - "seed": 815919225, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1700421905506, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 3, - "text": "Commune\nServer", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "igO3c0IESW8ZSDcllatP-", - "originalText": "Commune\nServer", - "lineHeight": 1.2, - "baseline": 43 - }, - { - "id": "jR76iIplo4TVvNKZBH6vT", - "type": "arrow", - "x": 495, - "y": 328.38774886792805, - "width": 86, - "height": 0.220972375229735, - "angle": 0, - "strokeColor": "#fa5252", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1227684473, - "version": 56, - "versionNonce": 1745929623, - "isDeleted": false, - "boundElements": null, - "updated": 1700421911263, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 86, - -0.220972375229735 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "RYL6z1yNI4ryhDrjxemyL", - "focus": -0.04931816566337021, - "gap": 1 - }, - "endBinding": { - "elementId": "igO3c0IESW8ZSDcllatP-", - "focus": 0.016806722689075442, - "gap": 1 - }, - "startArrowhead": null, - "endArrowhead": "triangle" - }, - { - "id": "BBin9aP3kMIsghlY9Q3Xb", - "type": "rectangle", - "x": 803, - "y": 235, - "width": 305, - "height": 200, - "angle": 0, - "strokeColor": "#868e96", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1920549721, - "version": 263, - "versionNonce": 2133981879, - "isDeleted": false, - "boundElements": [ - { - "id": "I--VThaGzeWEpMH77IyuH", - "type": "arrow" - } - ], - "updated": 1700421911263, - "link": null, - "locked": false - }, - { - "id": "mxgPLlWP_Rswux186mo6F", - "type": "image", - "x": 906, - "y": 149, - "width": 85, - "height": 85, - "angle": 0, - "strokeColor": "transparent", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1091137593, - "version": 301, - "versionNonce": 1118800855, - "isDeleted": false, - "boundElements": null, - "updated": 1700421911263, - "link": null, - "locked": false, - "status": "saved", - "fileId": "674c6c1c00fa6878a440ca4cb9c65ee6ce0b9d51", - "scale": [ - 1, - 1 - ] - }, - { - "id": "4N7vb2roINeGhvAK-EnUr", - "type": "rectangle", - "x": 831, - "y": 278, - "width": 114, - "height": 108, - "angle": 0, - "strokeColor": "#6741d9", - "backgroundColor": "#d0bfff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 717291801, - "version": 341, - "versionNonce": 513669367, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "4YZ1HMXFK8M48eFnDMR4v" - } - ], - "updated": 1700421911263, - "link": null, - "locked": false - }, - { - "id": "4YZ1HMXFK8M48eFnDMR4v", - "type": "text", - "x": 846.984375, - "y": 308, - "width": 82.03125, - "height": 48, - "angle": 0, - "strokeColor": "#6741d9", - "backgroundColor": "#d0bfff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1393907641, - "version": 262, - "versionNonce": 1356851097, - "isDeleted": false, - "boundElements": null, - "updated": 1700421905507, - "link": null, - "locked": false, - "text": "Matrix\nSynapse", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "4N7vb2roINeGhvAK-EnUr", - "originalText": "Matrix\nSynapse", - "lineHeight": 1.2 - }, - { - "type": "rectangle", - "version": 494, - "versionNonce": 142721559, - "isDeleted": false, - "id": "9lVssKY2uk0fr5TCbd5kT", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 969, - "y": 277, - "strokeColor": "#6741d9", - "backgroundColor": "#d0bfff", - "width": 114, - "height": 108, - "seed": 487814649, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "QJwL5UFm3QWsAGT_IMZZG" - } - ], - "updated": 1700421911263, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 431, - "versionNonce": 462523513, - "isDeleted": false, - "id": "QJwL5UFm3QWsAGT_IMZZG", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 979.125, - "y": 321.4, - "strokeColor": "#6741d9", - "backgroundColor": "#d0bfff", - "width": 93.75, - "height": 19.2, - "seed": 1463634583, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1700421905508, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 3, - "text": "PostgreSQL", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "9lVssKY2uk0fr5TCbd5kT", - "originalText": "PostgreSQL", - "lineHeight": 1.2, - "baseline": 15 - }, - { - "type": "arrow", - "version": 257, - "versionNonce": 1364158263, - "isDeleted": false, - "id": "I--VThaGzeWEpMH77IyuH", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 714, - "y": 329.467361039966, - "strokeColor": "#e8590c", - "backgroundColor": "#a5d8ff", - "width": 86, - "height": 0.2401904503404353, - "seed": 184831705, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1700421911263, - "link": null, - "locked": false, - "startBinding": { - "elementId": "igO3c0IESW8ZSDcllatP-", - "gap": 2, - "focus": 0.010959919320073687 - }, - "endBinding": { - "elementId": "BBin9aP3kMIsghlY9Q3Xb", - "gap": 3, - "focus": 0.06180802042331033 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - 86, - -0.2401904503404353 - ] - ] - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": { - "674c6c1c00fa6878a440ca4cb9c65ee6ce0b9d51": { - "mimeType": "image/png", - "id": "674c6c1c00fa6878a440ca4cb9c65ee6ce0b9d51", - "dataURL": "data:image/png;base64,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", - "created": 1700421758712, - "lastRetrieved": 1700421758712 - } - } -} \ No newline at end of file diff --git a/docs/diagrams/diagram.png b/docs/diagrams/diagram.png deleted file mode 100644 index bb027bd0d30454880fb425ab3cc483dfade75747..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27142 zcmeFZg;!Kv*fvfINVjx@inMfhw+PbGJ#@oR64D}_3W{_ybazX4cXtiU{6>8qpZEQJ zf5Erbx7L}N!?T{P!H5^H)QP5^)qbI59YRDe*U+@Q2MP#`@j$*U&5jLf-npW-?;^-6s;f zI0~o~pW-%`)$rQH@YpxkvCnrdlF7nd@W~z688GFTaHwelLu@8|Ji6;{49(uXofC%M zq*hf4=oTF+%umf#jkeua7qq^UA06`Ap}>8DM-Bh~j{hq)V8sBRpSYJ2g(Mg7&+#-V z;GiMe=6SEvV$g?=_EqBzcvoUVk~#H9dQ(`JS(xBAMJ!}e;*7xNu$r<0>?8b9vtIpU8A}=X@Bt7A2qp;s8g|j z;1a%z_J1Zv&5rc^7IA{G7!@rJ?)M?)kHfBckH(Bf-lW;9zE)NR!Jx-^jX>}qet

HbrMT<{$e%tV_%-xB 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 Y3gTKPXKX%w@f%DEPm~`Q$r}3oFRc-P!T impl IntoResponse { + let _ = request + .extract::, _>() + .await + .map_err(|_error| {}); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c05b5dc --- /dev/null +++ b/src/main.rs @@ -0,0 +1,7 @@ +use axum::{routing::post, Router}; + +pub(crate) mod api; + +fn main() { + let _router = Router::<()>::new().route("/ping", post(api::ping_route)); +}