add basic migrations
This commit is contained in:
		
							parent
							
								
									00eca2801f
								
							
						
					
					
						commit
						97d089c284
					
				
					 10 changed files with 251 additions and 29 deletions
				
			
		
							
								
								
									
										125
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										125
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -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" | ||||
|  |  | |||
							
								
								
									
										3
									
								
								config.identity.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								config.identity.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| database_url = "postgresql://foxchat:password@localhost/foxchat_ident_dev" | ||||
| port = 3000 | ||||
| log_level = "DEBUG" | ||||
|  | @ -10,9 +10,11 @@ pub enum Payload { | |||
|         recipients: Vec<String>, | ||||
|     }, | ||||
|     Hello, | ||||
|     Identify { token: String }, | ||||
|     Identify { | ||||
|         token: String, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| #[serde(tag = "t", content = "d", rename_all = "SCREAMING_SNAKE_CASE")] | ||||
| pub enum DispatchEvent {} | ||||
| pub enum DispatchEvent {} | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| mod event; | ||||
| 
 | ||||
| pub use event::{Payload, DispatchEvent}; | ||||
| pub use event::{DispatchEvent, Payload}; | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
							
								
								
									
										54
									
								
								identity/src/config.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								identity/src/config.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<bool>, | ||||
|     pub log_level: Option<String>, | ||||
| } | ||||
| 
 | ||||
| impl Config { | ||||
|     pub fn load() -> Result<Self> { | ||||
|         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<Level> { | ||||
|         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, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -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<Pool<Postgres>> { | ||||
| pub async fn init(dsn: &str) -> Result<Pool<Postgres>> { | ||||
|     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<Postgres>) -> Result<()> { | ||||
| pub async fn init_instance(pool: &Pool<Postgres>) -> 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<Postgres>) -> 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(()) | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -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<Command>, | ||||
| } | ||||
| 
 | ||||
| #[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(()) | ||||
| } | ||||
|  |  | |||
|  | @ -15,4 +15,4 @@ pub struct Account { | |||
| pub enum Role { | ||||
|     User, | ||||
|     Admin, | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -13,4 +13,4 @@ pub struct ChatInstance { | |||
| pub enum InstanceStatus { | ||||
|     Active, | ||||
|     Suspended, | ||||
| } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue