diff --git a/Cargo.lock b/Cargo.lock index 4fcbbce..251c781 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "aliasable" version = "0.1.3" @@ -63,6 +72,113 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "num_cpus", + "once_cell", + "tokio", +] + +[[package]] +name = "async-io" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + [[package]] name = "async-stream" version = "0.3.3" @@ -84,6 +200,12 @@ dependencies = [ "syn", ] +[[package]] +name = "async-task" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" + [[package]] name = "async-trait" version = "0.1.56" @@ -104,6 +226,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -231,6 +370,20 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + [[package]] name = "bumpalo" version = "3.10.0" @@ -249,6 +402,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + [[package]] name = "cc" version = "1.0.73" @@ -275,6 +434,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + [[package]] name = "const-oid" version = "0.6.2" @@ -357,6 +540,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "curve25519-dalek" version = "3.2.1" @@ -557,6 +750,21 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.21" @@ -630,6 +838,18 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gloo-timers" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -796,6 +1016,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "js_int" version = "0.2.2" @@ -814,6 +1043,15 @@ dependencies = [ "serde", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -854,6 +1092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", + "value-bag", ] [[package]] @@ -883,29 +1122,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" -[[package]] -name = "matrix" -version = "0.1.0" -dependencies = [ - "anyhow", - "argon2", - "axum", - "axum-macros", - "http", - "neo-entity", - "rand 0.8.5", - "ruma", - "sea-orm", - "serde", - "serde_json", - "thiserror", - "tokio", - "tower-http 0.2.5", - "tracing", - "tracing-subscriber", - "uuid 1.1.2", -] - [[package]] name = "memchr" version = "2.5.0" @@ -954,6 +1170,30 @@ dependencies = [ "tempfile", ] +[[package]] +name = "neo" +version = "0.1.0" +dependencies = [ + "anyhow", + "argon2", + "axum", + "axum-macros", + "http", + "neo-entity", + "neo-migration", + "rand 0.8.5", + "ruma", + "sea-orm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower-http 0.2.5", + "tracing", + "tracing-subscriber", + "uuid 1.1.2", +] + [[package]] name = "neo-entity" version = "0.1.0" @@ -965,6 +1205,14 @@ dependencies = [ "uuid 1.1.2", ] +[[package]] +name = "neo-migration" +version = "0.1.0" +dependencies = [ + "neo-entity", + "sea-orm-migration", +] + [[package]] name = "nom" version = "7.1.1" @@ -1096,6 +1344,12 @@ dependencies = [ "syn", ] +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.11.2" @@ -1216,6 +1470,19 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "polling" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1366,6 +1633,8 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -1577,6 +1846,23 @@ dependencies = [ "uuid 0.8.2", ] +[[package]] +name = "sea-orm-cli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca862fdba12c753bffba9c9adf95d3d3f5dcc82fd589b12faeee7068bb173d5" +dependencies = [ + "async-std", + "chrono", + "clap", + "dotenv", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", +] + [[package]] name = "sea-orm-macros" version = "0.8.0" @@ -1590,6 +1876,22 @@ dependencies = [ "syn", ] +[[package]] +name = "sea-orm-migration" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15589f057677f57bea393572bd8eb9e8feb843a5f09b4fa518be6cef3a6ffedc" +dependencies = [ + "async-trait", + "clap", + "dotenv", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + [[package]] name = "sea-query" version = "0.24.6" @@ -1629,6 +1931,29 @@ dependencies = [ "syn", ] +[[package]] +name = "sea-schema" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01a736b624984160b22a10a6d8efcb95958af68d3dd0a06a529092056ddc902" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sea-strum" version = "0.23.0" @@ -2011,6 +2336,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "subtle" version = "2.4.1" @@ -2060,6 +2391,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.31" @@ -2382,6 +2722,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "unicode-xid" version = "0.2.3" @@ -2432,18 +2778,40 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "want" version = "0.3.0" @@ -2497,6 +2865,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.81" @@ -2526,6 +2906,25 @@ version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +[[package]] +name = "web-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "wildmatch" version = "2.1.1" diff --git a/Cargo.toml b/Cargo.toml index 7a84e4b..b3c9ee9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ members = [ "neo", - "neo-entity" + "neo-entity", + "neo-migration" ] diff --git a/neo-entity/src/devices.rs b/neo-entity/src/devices.rs index a791788..532e8d8 100644 --- a/neo-entity/src/devices.rs +++ b/neo-entity/src/devices.rs @@ -1,4 +1,5 @@ use sea_orm::entity::prelude::*; +use sea_orm::Set; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "devices")] @@ -36,4 +37,12 @@ impl Related for Entity { } } -impl ActiveModelBehavior for ActiveModel {} +impl ActiveModelBehavior for ActiveModel { + fn new() -> Self { + Self { + uuid: Set(Uuid::new_v4()), + device_id: Set(Uuid::new_v4().to_string()), + ..ActiveModelTrait::default() + } + } +} diff --git a/neo-entity/src/lib.rs b/neo-entity/src/lib.rs index bb93954..7ce4e85 100644 --- a/neo-entity/src/lib.rs +++ b/neo-entity/src/lib.rs @@ -1,3 +1,4 @@ pub mod users; pub mod devices; -pub mod sessions; \ No newline at end of file +pub mod sessions; +pub mod prelude; diff --git a/neo-entity/src/prelude.rs b/neo-entity/src/prelude.rs new file mode 100644 index 0000000..8560986 --- /dev/null +++ b/neo-entity/src/prelude.rs @@ -0,0 +1,6 @@ +#[allow(unused_imports)] +pub use crate::{ + devices::{self, Entity as Device}, + sessions::{self, Entity as Session}, + users::{self, Entity as User}, +}; diff --git a/neo-entity/src/users.rs b/neo-entity/src/users.rs index a6e4110..b44c479 100644 --- a/neo-entity/src/users.rs +++ b/neo-entity/src/users.rs @@ -1,4 +1,5 @@ use sea_orm::entity::prelude::*; +use sea_orm::Set; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "users")] @@ -13,7 +14,7 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm(has_many = "super::devices::Entity")] - Devices + Devices, } impl Related for Entity { @@ -22,4 +23,11 @@ impl Related for Entity { } } -impl ActiveModelBehavior for ActiveModel {} +impl ActiveModelBehavior for ActiveModel { + fn new() -> Self { + Self { + uuid: Set(Uuid::new_v4()), + ..ActiveModelTrait::default() + } + } +} diff --git a/neo-migration/Cargo.toml b/neo-migration/Cargo.toml new file mode 100644 index 0000000..a053534 --- /dev/null +++ b/neo-migration/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "neo-migration" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +sea-orm-migration = "^0.8" +neo-entity = { path = "../neo-entity" } diff --git a/neo-migration/README.md b/neo-migration/README.md new file mode 100644 index 0000000..963caae --- /dev/null +++ b/neo-migration/README.md @@ -0,0 +1,37 @@ +# Running Migrator CLI + +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/neo-migration/src/lib.rs b/neo-migration/src/lib.rs new file mode 100644 index 0000000..dde53f7 --- /dev/null +++ b/neo-migration/src/lib.rs @@ -0,0 +1,18 @@ +pub use sea_orm_migration::prelude::*; + +mod m20220707_092851_create_users; +mod m20220707_112339_create_devices; +mod m20220707_143304_create_sessions; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20220707_092851_create_users::Migration), + Box::new(m20220707_112339_create_devices::Migration), + Box::new(m20220707_143304_create_sessions::Migration), + ] + } +} diff --git a/neo-migration/src/m20220707_092851_create_users.rs b/neo-migration/src/m20220707_092851_create_users.rs new file mode 100644 index 0000000..784a50d --- /dev/null +++ b/neo-migration/src/m20220707_092851_create_users.rs @@ -0,0 +1,49 @@ +use neo_entity::users::{self, Entity as User}; +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220707_092851_create_users" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(User) + .if_not_exists() + .col( + ColumnDef::new(users::Column::Uuid) + .uuid() + .primary_key() + .not_null(), + ) + .col(ColumnDef::new(users::Column::UserId).string().not_null()) + .col( + ColumnDef::new(users::Column::DisplayName) + .string() + .not_null(), + ) + .col( + ColumnDef::new(users::Column::PasswordHash) + .string() + .not_null(), + ) + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .table(User) + .col(users::Column::UserId) + .to_owned(), + ) + .await + } +} diff --git a/neo-migration/src/m20220707_112339_create_devices.rs b/neo-migration/src/m20220707_112339_create_devices.rs new file mode 100644 index 0000000..74d19e3 --- /dev/null +++ b/neo-migration/src/m20220707_112339_create_devices.rs @@ -0,0 +1,57 @@ +use neo_entity::devices::{self, Entity as Device}; +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220707_112339_create_devices.rs" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Device) + .if_not_exists() + .col( + ColumnDef::new(devices::Column::Uuid) + .uuid() + .primary_key() + .not_null(), + ) + .col(ColumnDef::new(devices::Column::UserUuid).uuid().not_null()) + .col( + ColumnDef::new(devices::Column::DeviceId) + .string() + .not_null(), + ) + .col( + ColumnDef::new(devices::Column::DisplayName) + .string() + .not_null(), + ) + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .table(Device) + .col(devices::Column::DeviceId) + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .table(Device) + .col(devices::Column::UserUuid) + .to_owned(), + ) + .await + } +} diff --git a/neo-migration/src/m20220707_143304_create_sessions.rs b/neo-migration/src/m20220707_143304_create_sessions.rs new file mode 100644 index 0000000..39b54cb --- /dev/null +++ b/neo-migration/src/m20220707_143304_create_sessions.rs @@ -0,0 +1,44 @@ +use neo_entity::sessions::{self, Entity as Session}; +use sea_orm_migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220707_143304_create_sessions.rs" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Session) + .if_not_exists() + .col( + ColumnDef::new(sessions::Column::Uuid) + .uuid() + .primary_key() + .not_null(), + ) + .col( + ColumnDef::new(sessions::Column::DeviceUuid) + .uuid() + .not_null(), + ) + .col(ColumnDef::new(sessions::Column::Key).string().not_null()) + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .table(Session) + .col(sessions::Column::DeviceUuid) + .to_owned(), + ) + .await + } +} diff --git a/neo/Cargo.toml b/neo/Cargo.toml index 68c8154..f4751cd 100644 --- a/neo/Cargo.toml +++ b/neo/Cargo.toml @@ -1,10 +1,8 @@ [package] -name = "matrix" +name = "neo" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] tokio = { version = "1.17", features = ["full"] } axum = "0.5" @@ -13,7 +11,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" tower-http = { version = "0.2", features = ["cors", "trace"]} -#sqlx = { version = "0.5", features = ["sqlite", "macros", "runtime-tokio-rustls", "offline"] } anyhow = "1.0" thiserror = "1.0" argon2 = { version = "0.4", features = ["std"] } @@ -23,4 +20,5 @@ ruma = { version = "0.6.4", features = ["client-api", "compat"] } axum-macros = "0.2.2" http = "0.2.8" sea-orm = { version = "^0.8", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros"], default-features = false } -neo-entity = { version = "*", path = "../neo-entity" } \ No newline at end of file +neo-entity = { version = "*", path = "../neo-entity" } +neo-migration = { version = "*", path = "../neo-migration" } diff --git a/neo/src/api/client_server/errors/api_error.rs b/neo/src/api/client_server/errors/api_error.rs index f2770e2..8d09122 100644 --- a/neo/src/api/client_server/errors/api_error.rs +++ b/neo/src/api/client_server/errors/api_error.rs @@ -27,7 +27,7 @@ pub enum ApiError { AuthenticationError(#[from] AuthenticationError), #[error("Database Error")] - DBError(#[from] sqlx::Error), + DBError(#[from] sea_orm::DbErr), #[error("Generic Error")] Generic(anyhow::Error), @@ -36,7 +36,7 @@ pub enum ApiError { impl From for ApiError { fn from(err: anyhow::Error) -> Self { map_err!(err, - sqlx::Error => ApiError::DBError, + sea_orm::DbErr => ApiError::DBError, RegistrationError => ApiError::RegistrationError, AuthenticationError => ApiError::AuthenticationError ); diff --git a/neo/src/api/client_server/r0/auth.rs b/neo/src/api/client_server/r0/auth.rs index c191264..7627c48 100644 --- a/neo/src/api/client_server/r0/auth.rs +++ b/neo/src/api/client_server/r0/auth.rs @@ -5,11 +5,13 @@ use axum::{ routing::{get, post}, Extension, }; -use neo_entity::{ - devices::{self, Entity as Device}, - sessions::{self, Entity as Session}, - users::{self, Entity as User}, -}; +// use neo_entity::{ +// devices::{self, Entity as Device}, +// sessions::{self, Entity as Session}, +// users::{self, Entity as User}, +// }; + +use neo_entity::prelude::*; use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, Set}; use crate::{ @@ -66,7 +68,7 @@ async fn login( .one(&db) .await? .ok_or(AuthenticationError::InvalidUserId)?; - //todo check password + // TODO: check password //db_user // .password_correct(&password) // .map_err(|_| AuthenticationError::Forbidden)?; @@ -95,7 +97,7 @@ async fn login( }; device.insert(&db).await? }; - + let session = sessions::ActiveModel { device_uuid: Set(device.uuid), ..Default::default() @@ -123,7 +125,8 @@ async fn get_username_available( let username = params .get("username") - .ok_or(RegistrationError::MissingUserId)?.to_owned(); + .ok_or(RegistrationError::MissingUserId)? + .to_owned(); let user_id = ruma::UserId::parse_with_server_name(username, &config.server_name) .map_err(|_| RegistrationError::InvalidUserId)?; @@ -161,30 +164,44 @@ async fn post_register( return Err(AuthenticationError::InvalidUserId.into()); }; - if User::exists(&db, &user_id).await? { - return Err(ApiError::from(RegistrationError::UserIdTaken)); - } + User::find() + .filter(users::Column::UserId.eq(user_id.as_str())) + .one(&db) + .await? + .ok_or(RegistrationError::UserIdTaken)?; let display_name = req .initial_device_display_name .unwrap_or_else(|| "Random Display Name".into()); - let user = User::new(&user_id, &user_id.to_string(), &password)? - .create(&db) - .await?; - - let device = Device::new( - &user, - uuid::Uuid::new_v4().to_string().as_ref(), - &display_name, - )? - .create(&db) + // TODO: Hash password + let user = users::ActiveModel { + user_id: Set(user_id.to_string()), + display_name: Set(user_id.to_string()), + password_hash: Set(password), + ..Default::default() + } + .insert(&db) .await?; + + let device = devices::ActiveModel { + display_name: Set(display_name), + user_uuid: Set(user.uuid), + ..Default::default() + } + .insert(&db) + .await?; + let mut response = Response::new( ruma::UserId::parse(&user.user_id).map_err(|e| anyhow::anyhow!(e))?, ); if !req.inhibit_login { - let session = Session::new(&device)?.create(&db).await?; + let session = sessions::ActiveModel { + device_uuid: Set(device.uuid), + ..Default::default() + } + .insert(&db) + .await?; response.access_token = Some(session.key); } if !req.inhibit_login { @@ -202,36 +219,48 @@ async fn post_register( .ok_or("password missing") .map_err(|_| RegistrationError::AdditionalAuthenticationInformation)?; let user_id = if let Some(username) = req.username { - let user_id = UserId::new(&username, config.server_name())?; - - ruma::UserId::parse(user_id.to_string()).map_err(|e| anyhow::anyhow!(e))? + ruma::UserId::parse_with_server_name(username, &config.server_name) + .map_err(|e| anyhow::anyhow!(e))? } else { return Err(AuthenticationError::InvalidUserId.into()); }; - if User::exists(&db, &user_id).await? { - return Err(RegistrationError::UserIdTaken.into()); - } + User::find() + .filter(users::Column::UserId.eq(user_id.as_str())) + .one(&db) + .await? + .ok_or(RegistrationError::UserIdTaken)?; let display_name = req .initial_device_display_name .unwrap_or_else(|| "Random Display Name".into()); - let user = User::new(&user_id, &user_id.to_string(), &password)? - .create(&db) - .await?; - - let device = Device::new( - &user, - uuid::Uuid::new_v4().to_string().as_ref(), - &display_name, - )? - .create(&db) + let user = users::ActiveModel { + user_id: Set(user_id.to_string()), + display_name: Set(user_id.to_string()), + password_hash: Set(password), + ..Default::default() + } + .insert(&db) .await?; + + let device = devices::ActiveModel { + display_name: Set(display_name), + user_uuid: Set(user.uuid), + ..Default::default() + } + .insert(&db) + .await?; + let mut response = Response::new(ruma::UserId::parse(&user.user_id).map_err(|e| anyhow::anyhow!(e))?); if !req.inhibit_login { - let session = Session::new(&device)?.create(&db).await?; + let session = sessions::ActiveModel { + device_uuid: Set(device.uuid), + ..Default::default() + } + .insert(&db) + .await?; response.access_token = Some(session.key); } if !req.inhibit_login { diff --git a/neo/src/api/client_server/r0/filter.rs b/neo/src/api/client_server/r0/filter.rs index f0d1f90..6cfa130 100644 --- a/neo/src/api/client_server/r0/filter.rs +++ b/neo/src/api/client_server/r0/filter.rs @@ -4,7 +4,7 @@ use axum::routing::post; use axum::Extension; use crate::api::client_server::errors::api_error::ApiError; -use crate::models::users::User; +use neo_entity::prelude::*; use crate::ruma_wrapper::{RumaRequest, RumaResponse}; use ruma::api::client::filter; diff --git a/neo/src/api/client_server/r0/keys.rs b/neo/src/api/client_server/r0/keys.rs index 9771d69..f403064 100644 --- a/neo/src/api/client_server/r0/keys.rs +++ b/neo/src/api/client_server/r0/keys.rs @@ -4,7 +4,7 @@ use axum::routing::post; use axum::Extension; use crate::api::client_server::errors::api_error::ApiError; -use crate::models::users::User; +use neo_entity::prelude::*; use crate::ruma_wrapper::{RumaRequest, RumaResponse}; use ruma::api::client::keys; diff --git a/neo/src/api/client_server/r0/presence.rs b/neo/src/api/client_server/r0/presence.rs index 4866f63..ad5e897 100644 --- a/neo/src/api/client_server/r0/presence.rs +++ b/neo/src/api/client_server/r0/presence.rs @@ -4,7 +4,7 @@ use axum::routing::{get, put}; use axum::Extension; use crate::api::client_server::errors::api_error::ApiError; -use crate::models::users::User; +use neo_entity::prelude::*; use crate::ruma_wrapper::{RumaRequest, RumaResponse}; use ruma::api::client::presence; diff --git a/neo/src/api/client_server/r0/push.rs b/neo/src/api/client_server/r0/push.rs index 57e2255..9bd8596 100644 --- a/neo/src/api/client_server/r0/push.rs +++ b/neo/src/api/client_server/r0/push.rs @@ -4,7 +4,7 @@ use axum::routing::get; use axum::Extension; use crate::api::client_server::errors::api_error::ApiError; -use crate::models::users::User; +use neo_entity::prelude::*; use crate::ruma_wrapper::{RumaRequest, RumaResponse}; use ruma::api::client::push; @@ -18,6 +18,7 @@ pub fn routes() -> axum::Router { async fn get_pushrules( Extension(_user): Extension>, + RumaRequest(_req): RumaRequest ) -> Result, ApiError> { use push::get_pushrules_all::v3::*; diff --git a/neo/src/api/client_server/r0/sync.rs b/neo/src/api/client_server/r0/sync.rs index 85672f7..db50568 100644 --- a/neo/src/api/client_server/r0/sync.rs +++ b/neo/src/api/client_server/r0/sync.rs @@ -4,8 +4,8 @@ use axum::routing::get; use axum::Extension; use crate::api::client_server::errors::api_error::ApiError; -use crate::models::users::User; use crate::ruma_wrapper::{RumaRequest, RumaResponse}; +use neo_entity::prelude::*; use ruma::api::client::sync; diff --git a/neo/src/api/client_server/r0/thirdparty.rs b/neo/src/api/client_server/r0/thirdparty.rs index 5444986..742e74a 100644 --- a/neo/src/api/client_server/r0/thirdparty.rs +++ b/neo/src/api/client_server/r0/thirdparty.rs @@ -2,8 +2,10 @@ use std::{collections::BTreeMap, sync::Arc}; use axum::{routing::get, Extension}; +use neo_entity::prelude::*; + use crate::{ - api::client_server::errors::api_error::ApiError, models::users::User, + api::client_server::errors::api_error::ApiError, ruma_wrapper::RumaResponse, }; diff --git a/neo/src/main.rs b/neo/src/main.rs index 037c3a8..0e09971 100644 --- a/neo/src/main.rs +++ b/neo/src/main.rs @@ -13,10 +13,10 @@ use tower_http::{ trace::{DefaultMakeSpan, DefaultOnRequest, DefaultOnResponse, TraceLayer}, }; use tracing::Level; +use neo_migration::{Migrator, MigratorTrait}; mod api; mod config; -mod models; mod responses; mod ruma_wrapper; mod types; @@ -31,7 +31,8 @@ async fn main() -> anyhow::Result<()> { let config = Arc::new(Config::default()); - let pool = Database::connect(config.db_path).await?; + let pool = Database::connect(config.db_path.clone()).await?; + Migrator::up(&pool, None).await?; // TODO: set correct CORS headers let cors = CorsLayer::new() diff --git a/neo/src/models/devices.rs b/neo/src/models/devices.rs deleted file mode 100644 index 9e9d213..0000000 --- a/neo/src/models/devices.rs +++ /dev/null @@ -1,70 +0,0 @@ -use sqlx::SqlitePool; - -use crate::types::uuid::Uuid; - -use super::users::User; - -pub struct Device { - pub uuid: Uuid, - pub user_uuid: Uuid, - pub device_id: String, - pub display_name: String, -} - -impl Device { - pub fn new(user: &User, device_id: &str, display_name: &str) -> anyhow::Result { - Ok(Self { - uuid: uuid::Uuid::new_v4().into(), - user_uuid: user.uuid.clone(), - device_id: device_id.to_owned(), - display_name: display_name.to_owned(), - }) - } - - pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "insert into devices(uuid, user_uuid, device_id, display_name) - values(?, ?, ?, ?) - returning uuid as 'uuid: Uuid', user_uuid as 'user_uuid: Uuid', device_id, display_name", - self.uuid, - self.user_uuid, - self.device_id, - self.display_name) - .fetch_one(conn).await? - ) - } - - pub async fn find_for_user( - conn: &SqlitePool, - user: &User, - device_id: &str, - ) -> anyhow::Result { - let user_uuid = user.uuid.clone(); - Ok(sqlx::query_as!( - Self, - "select uuid as 'uuid: Uuid', user_uuid as 'user_uuid: Uuid', device_id, display_name from devices where user_uuid = ? and device_id = ?", - user_uuid, device_id) - .fetch_one(conn).await? - ) - } - - pub async fn find_by_uuid(conn: &SqlitePool, uuid: &Uuid) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "select uuid as 'uuid: Uuid', user_uuid as 'user_uuid: Uuid', device_id, display_name from devices where uuid = ?", - uuid) - .fetch_one(conn).await? - ) - } - - pub async fn user(&self, conn: &SqlitePool) -> anyhow::Result { - User::find_by_uuid(conn, &self.user_uuid).await - } - - /// Get the device's id. - #[must_use] - pub fn uuid(&self) -> &Uuid { - &self.uuid - } -} diff --git a/neo/src/models/events.rs b/neo/src/models/events.rs deleted file mode 100644 index 5c90909..0000000 --- a/neo/src/models/events.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* use sqlx::SqlitePool; - -use crate::types::{uuid::Uuid, event_type::EventType}; - -use super::{users::User}; - -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct Event { - uuid: Uuid, - room_uuid: Uuid, - r#type: EventType, - state_key: Option, - sender_uuid: Uuid, - origin_server_ts: i64, - content: String, -} - -impl Event { - fn new( - room: &Room, - r#type: EventType, - state_key: Option, - sender: &User, - origin_server_ts: i64, - content: &str, - ) -> anyhow::Result { - Ok(Self { - uuid: uuid::Uuid::new_v4().into(), - room_uuid: room.uuid().to_owned(), - state_key, - r#type, - sender_uuid: sender.uuid().to_owned(), - origin_server_ts, - content: content.to_owned(), - }) - } - - pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "insert into events(uuid, room_uuid, type, state_key, sender_uuid, origin_server_ts, content) - values(?, ?, ?, ?, ?, ?, ?) - returning uuid as 'uuid: Uuid', room_uuid as 'room_uuid: Uuid', type as 'type: EventType', state_key, sender_uuid as 'sender_uuid: Uuid', origin_server_ts, content", - self.uuid, - self.room_uuid, - self.r#type, - self.state_key, - self.sender_uuid, - self.origin_server_ts, - self.content - ) - .fetch_one(conn) - .await?) - } - - pub async fn all_for_room(conn: &SqlitePool, room: &Room) -> anyhow::Result> { - let room_uuid = room.uuid(); - Ok(sqlx::query_as!( - Self, - "select uuid as 'uuid: Uuid', room_uuid as 'room_uuid: Uuid', type as 'type: EventType', state_key, sender_uuid as 'sender_uuid: Uuid', origin_server_ts, content - from events - where room_uuid = ?", - room_uuid - ) - .fetch_all(conn) - .await?) - } - - pub fn content(&self) -> serde_json::Value { - serde_json::from_str(&self.content).expect("has to be valid json") - } -} - */ diff --git a/neo/src/models/mod.rs b/neo/src/models/mod.rs deleted file mode 100644 index 61b1e5c..0000000 --- a/neo/src/models/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod devices; -pub mod events; -pub mod sessions; -pub mod users; diff --git a/neo/src/models/sessions.rs b/neo/src/models/sessions.rs deleted file mode 100644 index 70c4b4c..0000000 --- a/neo/src/models/sessions.rs +++ /dev/null @@ -1,54 +0,0 @@ -use rand::{distributions::Alphanumeric, thread_rng, Rng}; -use sqlx::SqlitePool; - -use crate::types::uuid::Uuid; - -use super::devices::Device; - -pub struct Session { - pub uuid: Uuid, - pub device_uuid: Uuid, - pub key: String, -} - -impl Session { - pub fn new(device: &Device) -> anyhow::Result { - let mut rng = thread_rng(); - let key: String = (0..256).map(|_| rng.sample(Alphanumeric) as char).collect(); - - Ok(Self { - uuid: uuid::Uuid::new_v4().into(), - device_uuid: device.uuid().clone(), - key, - }) - } - - pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "insert into sessions(uuid, device_uuid, key) - values(?, ?, ?) - returning uuid as 'uuid: Uuid', device_uuid as 'device_uuid: Uuid', key", - self.uuid, - self.device_uuid, - self.key - ) - .fetch_one(conn) - .await?) - } - - pub async fn find_by_key(conn: &SqlitePool, key: &str) -> anyhow::Result> { - Ok(sqlx::query_as!( - Self, - "select uuid as 'uuid: Uuid', device_uuid as 'device_uuid: Uuid', key - from sessions where key = ?", - key - ) - .fetch_optional(conn) - .await?) - } - - pub async fn device(&self, conn: &SqlitePool) -> anyhow::Result { - Device::find_by_uuid(conn, &self.device_uuid).await - } -} diff --git a/neo/src/models/users.rs b/neo/src/models/users.rs deleted file mode 100644 index 1bcad06..0000000 --- a/neo/src/models/users.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::types::uuid::Uuid; -use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; -use rand::rngs::OsRng; -use ruma::OwnedUserId; -use sqlx::SqlitePool; - -#[derive(Debug)] -pub struct User { - pub uuid: Uuid, - pub user_id: String, - pub display_name: String, - pub password_hash: String, -} - -impl User { - pub fn new(user_id: &OwnedUserId, display_name: &str, password: &str) -> anyhow::Result { - let argon2 = Argon2::default(); - let salt = SaltString::generate(OsRng); - let password_hash = argon2 - .hash_password(password.as_bytes(), &salt)? - .to_string(); - - Ok(Self { - uuid: uuid::Uuid::new_v4().into(), - user_id: user_id.to_string(), - display_name: display_name.to_owned(), - password_hash, - }) - } - - pub async fn exists(conn: &SqlitePool, user_id: &OwnedUserId) -> anyhow::Result { - let user_id = user_id.to_string(); - Ok( - sqlx::query!("select user_id from users where user_id = ?", user_id) - .fetch_optional(conn) - .await? - .is_some(), - ) - } - - pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "insert into users(uuid, user_id, display_name, password_hash) - values (?, ?, ?, ?) - returning uuid as 'uuid: Uuid', user_id, display_name, password_hash", - self.uuid, - self.user_id, - self.display_name, - self.password_hash - ) - .fetch_one(conn) - .await?) - } - - pub async fn update(&self, conn: &SqlitePool) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "update users set uuid = ?, user_id = ?, display_name = ?, password_hash = ? - where uuid = ? - returning uuid as 'uuid: Uuid', user_id, display_name, password_hash", - self.uuid, - self.user_id, - self.display_name, - self.password_hash, - self.uuid - ) - .fetch_one(conn) - .await?) - } - - pub async fn find_by_uuid(conn: &SqlitePool, uuid: &Uuid) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "select uuid as 'uuid: Uuid', user_id, display_name, password_hash - from users where uuid = ?", - uuid - ) - .fetch_one(conn) - .await?) - } - - pub async fn find_by_user_id(conn: &SqlitePool, user_id: &str) -> anyhow::Result { - Ok(sqlx::query_as!( - Self, - "select uuid as 'uuid: Uuid', user_id, display_name, password_hash - from users where user_id = ?", - user_id - ) - .fetch_one(conn) - .await?) - } - - pub fn password_correct(&self, password: &str) -> anyhow::Result { - let password_hash = PasswordHash::new(&self.password_hash)?; - - Ok(Argon2::default() - .verify_password(password.as_bytes(), &password_hash) - .is_ok()) - } -}