add basic migrations

This commit is contained in:
sam 2024-01-15 20:45:39 +01:00
parent 00eca2801f
commit 97d089c284
10 changed files with 251 additions and 29 deletions

View file

@ -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
View 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,
}
}
}

View file

@ -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(())
}
}

View file

@ -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(())
}

View file

@ -15,4 +15,4 @@ pub struct Account {
pub enum Role {
User,
Admin,
}
}

View file

@ -13,4 +13,4 @@ pub struct ChatInstance {
pub enum InstanceStatus {
Active,
Suspended,
}
}