From 97d089c28465c7fc2d2bf4a2f4b5a05e6c51362f Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 15 Jan 2024 20:45:39 +0100 Subject: [PATCH] add basic migrations --- Cargo.lock | 125 ++++++++++++++++++++++++++++ config.identity.toml | 3 + foxchat/src/s2s/event.rs | 6 +- foxchat/src/s2s/mod.rs | 2 +- identity/Cargo.toml | 4 + identity/src/config.rs | 54 ++++++++++++ identity/src/db/mod.rs | 31 ++++--- identity/src/main.rs | 51 +++++++++--- identity/src/model/account.rs | 2 +- identity/src/model/chat_instance.rs | 2 +- 10 files changed, 251 insertions(+), 29 deletions(-) create mode 100644 config.identity.toml create mode 100644 identity/src/config.rs diff --git a/Cargo.lock b/Cargo.lock index b34faca..1f107c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -725,6 +725,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + [[package]] name = "hex" version = "0.4.3" @@ -879,6 +885,10 @@ dependencies = [ "serde", "serde_json", "sqlx", + "tokio", + "toml", + "tracing", + "tracing-subscriber", "ulid", "uuid", ] @@ -1062,6 +1072,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1111,6 +1131,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.32.2" @@ -1126,6 +1156,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "3.5.0" @@ -1451,6 +1487,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1892,6 +1937,7 @@ dependencies = [ "bytes", "libc", "mio", + "num_cpus", "pin-project-lite", "socket2", "tokio-macros", @@ -1946,6 +1992,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -2017,15 +2097,29 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "nu-ansi-term", "sharded-slab", + "smallvec", "thread_local", "tracing-core", + "tracing-log", ] [[package]] @@ -2231,6 +2325,28 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" @@ -2372,6 +2488,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/config.identity.toml b/config.identity.toml new file mode 100644 index 0000000..0522bd2 --- /dev/null +++ b/config.identity.toml @@ -0,0 +1,3 @@ +database_url = "postgresql://foxchat:password@localhost/foxchat_ident_dev" +port = 3000 +log_level = "DEBUG" diff --git a/foxchat/src/s2s/event.rs b/foxchat/src/s2s/event.rs index b60be41..ca472d3 100644 --- a/foxchat/src/s2s/event.rs +++ b/foxchat/src/s2s/event.rs @@ -10,9 +10,11 @@ pub enum Payload { recipients: Vec, }, Hello, - Identify { token: String }, + Identify { + token: String, + }, } #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "t", content = "d", rename_all = "SCREAMING_SNAKE_CASE")] -pub enum DispatchEvent {} \ No newline at end of file +pub enum DispatchEvent {} diff --git a/foxchat/src/s2s/mod.rs b/foxchat/src/s2s/mod.rs index 0cfe2e9..1a46ca7 100644 --- a/foxchat/src/s2s/mod.rs +++ b/foxchat/src/s2s/mod.rs @@ -1,3 +1,3 @@ mod event; -pub use event::{Payload, DispatchEvent}; +pub use event::{DispatchEvent, Payload}; diff --git a/identity/Cargo.toml b/identity/Cargo.toml index a06a726..ddfa821 100644 --- a/identity/Cargo.toml +++ b/identity/Cargo.toml @@ -19,3 +19,7 @@ eyre = "0.6.11" color-eyre = "0.6.2" rsa = { version = "0.9.6", features = ["serde"] } rand = "0.8.5" +toml = "0.8.8" +tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } +tracing-subscriber = "0.3.18" +tracing = "0.1.40" diff --git a/identity/src/config.rs b/identity/src/config.rs new file mode 100644 index 0000000..fcdd23b --- /dev/null +++ b/identity/src/config.rs @@ -0,0 +1,54 @@ +use eyre::Result; +use serde::Deserialize; +use std::path::Path; +use std::{env, fs}; +use tracing::Level; + +pub const CONFIG_FILE: &str = "config.identity.toml"; + +#[derive(Deserialize)] +pub struct Config { + pub database_url: String, + pub port: u16, + pub auto_migrate: Option, + pub log_level: Option, +} + +impl Config { + pub fn load() -> Result { + let cwd = env::current_dir()?; + let config_file = Path::join(cwd.as_path(), Path::new(CONFIG_FILE)); + println!("config file: {}", config_file.display()); + + let s = fs::read_to_string(config_file)?; + let config = toml::from_str(s.as_str())?; + + Ok(config) + } + + pub fn tracing_level(&self) -> Option { + match self + .log_level + .as_deref() + .unwrap_or("INFO") + { + "TRACE" => Some(Level::TRACE), + "DEBUG" => Some(Level::DEBUG), + "INFO" => Some(Level::INFO), + "WARN" => Some(Level::WARN), + "ERROR" => Some(Level::ERROR), + _ => None + } + } +} + +impl Default for Config { + fn default() -> Self { + Config { + database_url: env::var("DATABASE_URL").unwrap_or("".into()), + port: 3000, + auto_migrate: None, + log_level: None, + } + } +} diff --git a/identity/src/db/mod.rs b/identity/src/db/mod.rs index 1ee1153..f287f36 100644 --- a/identity/src/db/mod.rs +++ b/identity/src/db/mod.rs @@ -1,30 +1,30 @@ -use std::time::Duration; use eyre::{OptionExt, Result}; -use rsa::{RsaPrivateKey, RsaPublicKey}; use rsa::pkcs1::LineEnding; use rsa::pkcs8::{EncodePrivateKey, EncodePublicKey}; -use sqlx::{Pool, Postgres}; +use rsa::{RsaPrivateKey, RsaPublicKey}; use sqlx::postgres::PgPoolOptions; +use sqlx::{Pool, Postgres}; +use std::time::Duration; -pub async fn init_db(dsn: &str) -> Result> { +pub async fn init(dsn: &str) -> Result> { let pool = PgPoolOptions::new() .acquire_timeout(Duration::from_secs(2)) // Fail fast and don't hang .max_connections(100) .connect(dsn) .await?; - init_instance(&pool)?; - Ok(pool) } const PRIVATE_KEY_BITS: usize = 2048; -async fn init_instance(pool: &Pool) -> Result<()> { +pub async fn init_instance(pool: &Pool) -> Result<()> { let mut tx = pool.begin().await?; // Check if we already have an instance configuration - let row = sqlx::query!("select exists(select * from instance)").fetch_one(&mut *tx).await?; + let row = sqlx::query!("select exists(select * from instance)") + .fetch_one(&mut *tx) + .await?; if row.exists.ok_or_eyre("exists was null")? { return Ok(()); } @@ -34,13 +34,18 @@ async fn init_instance(pool: &Pool) -> Result<()> { let priv_key = RsaPrivateKey::new(&mut rng, PRIVATE_KEY_BITS)?; let pub_key = RsaPublicKey::from(&priv_key); - let priv_key_string = priv_key.to_pkcs8_pem(LineEnding::default())?; - let pub_key_string = pub_key.to_public_key_pem(LineEnding::default())?; + let priv_key_string = priv_key.to_pkcs8_pem(LineEnding::LF)?; + let pub_key_string = pub_key.to_public_key_pem(LineEnding::LF)?; - sqlx::query!("insert into instance (public_key, private_key) values ($1, $2)", - priv_key_string.to_string(), pub_key_string).execute(&mut *tx).await?; + sqlx::query!( + "insert into instance (public_key, private_key) values ($1, $2)", + priv_key_string.to_string(), + pub_key_string + ) + .execute(&mut *tx) + .await?; tx.commit().await?; Ok(()) -} \ No newline at end of file +} diff --git a/identity/src/main.rs b/identity/src/main.rs index 2b4a088..da5effa 100644 --- a/identity/src/main.rs +++ b/identity/src/main.rs @@ -1,14 +1,16 @@ -mod model; +mod config; mod db; +mod model; -use color_eyre::eyre::Result; +use crate::config::Config; use clap::{Parser, Subcommand}; -use ulid::Ulid; +use color_eyre::eyre::Result; +use tracing::info; #[derive(Debug, Parser)] struct Cli { #[command(subcommand)] - command: Command, + command: Option, } #[derive(Debug, Subcommand)] @@ -17,18 +19,45 @@ enum Command { Migrate, } -fn main() -> Result<()> { +#[tokio::main] +async fn main() -> Result<()> { color_eyre::install()?; + let config = Config::load()?; let args = Cli::parse(); - println!("{args:#?}"); - println!("{}", match args.command { - Command::Serve => "serving!", - Command::Migrate => "migrating!" - }); + tracing_subscriber::fmt() + .with_max_level(config.tracing_level().unwrap_or(tracing::Level::INFO)) + .init(); - println!("{}", Ulid::new()); + match args.command.unwrap_or(Command::Serve) { + Command::Serve => main_web(config).await, + Command::Migrate => main_migrate(config).await, + } +} + +async fn main_migrate(config: Config) -> Result<()> { + info!("Connecting to database"); + let pool = db::init(&config.database_url).await?; + info!("Migrating database"); + sqlx::migrate!().run(&pool).await?; + info!("Migrated database"); + Ok(()) +} + +async fn main_web(config: Config) -> Result<()> { + info!("Connecting to database"); + let pool = db::init(&config.database_url).await?; + + if config.auto_migrate.unwrap_or(false) { + info!("Auto-migrate is enabled, migrating database"); + sqlx::migrate!().run(&pool).await?; + info!("Migrated database"); + } + + info!("Initializing instance data"); + db::init_instance(&pool).await?; + info!("Initialized instance data!"); Ok(()) } diff --git a/identity/src/model/account.rs b/identity/src/model/account.rs index 722a4ea..de1d25b 100644 --- a/identity/src/model/account.rs +++ b/identity/src/model/account.rs @@ -15,4 +15,4 @@ pub struct Account { pub enum Role { User, Admin, -} \ No newline at end of file +} diff --git a/identity/src/model/chat_instance.rs b/identity/src/model/chat_instance.rs index 3720ce8..d865c43 100644 --- a/identity/src/model/chat_instance.rs +++ b/identity/src/model/chat_instance.rs @@ -13,4 +13,4 @@ pub struct ChatInstance { pub enum InstanceStatus { Active, Suspended, -} \ No newline at end of file +}