v0.1.0
This commit is contained in:
+95
-2
@@ -10,7 +10,7 @@ mod state;
|
||||
mod storage;
|
||||
mod websocket;
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
|
||||
|
||||
use config::Config;
|
||||
use database::{Database, DatabaseKind};
|
||||
@@ -19,12 +19,18 @@ use tokio::net::TcpListener;
|
||||
use tracing::{info, warn};
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dotenvy::dotenv().ok();
|
||||
let cli = parse_command()?;
|
||||
init_tracing();
|
||||
|
||||
let config = Config::from_env()?;
|
||||
let config = Config::load(cli.config.as_deref())?;
|
||||
if matches!(cli.command, Command::CheckConfig) {
|
||||
println!("configuration is valid{}", cli.config.as_ref().map(|path| format!(" ({})", path.display())).unwrap_or_default());
|
||||
return Ok(());
|
||||
}
|
||||
info!(
|
||||
host = %config.host,
|
||||
port = config.port,
|
||||
@@ -61,6 +67,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!(database_kind = ?db.kind(), "database connection established");
|
||||
run_migrations(&db).await?;
|
||||
info!(database_kind = ?db.kind(), "database migrations completed");
|
||||
if matches!(cli.command, Command::Migrate) {
|
||||
println!("database migrations completed");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let storage = storage::Storage::from_config(config.storage.clone()).await?;
|
||||
info!(
|
||||
@@ -80,8 +90,23 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
config.frontend_log_level.clone(),
|
||||
config.anonymous_access_token_ttl_days,
|
||||
config.user_session_ttl_days,
|
||||
config.unconfirmed_account_ttl_days,
|
||||
config.ldap.clone(),
|
||||
));
|
||||
let cleanup_state = state.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(std::time::Duration::from_secs(24 * 60 * 60));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
let cutoff = (chrono::Utc::now() - chrono::Duration::days(cleanup_state.unconfirmed_account_ttl_days)).to_rfc3339();
|
||||
match sqlx::query(crate::queries::get(cleanup_state.db.kind(), crate::queries::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS))
|
||||
.bind(cutoff).execute(cleanup_state.db.pool()).await {
|
||||
Ok(result) if result.rows_affected() > 0 => info!(deleted = result.rows_affected(), "removed expired unconfirmed accounts"),
|
||||
Ok(_) => {},
|
||||
Err(error) => tracing::error!(%error, "failed to remove expired unconfirmed accounts"),
|
||||
}
|
||||
}
|
||||
});
|
||||
let app = app::router(
|
||||
state,
|
||||
&config.static_dir,
|
||||
@@ -99,6 +124,74 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Command { Run, CheckConfig, Migrate }
|
||||
|
||||
struct Cli {
|
||||
command: Command,
|
||||
config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn parse_command() -> Result<Cli, Box<dyn std::error::Error>> {
|
||||
let mut command = Command::Run;
|
||||
let mut config = None;
|
||||
let mut args = std::env::args().skip(1);
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"-v" | "--version" => {
|
||||
println!("rustpad {}", env!("CARGO_PKG_VERSION"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
"-h" | "--help" => {
|
||||
print_help();
|
||||
std::process::exit(0);
|
||||
}
|
||||
"-c" | "--config" => {
|
||||
let path = args.next().ok_or("--config requires a file path")?;
|
||||
if config.replace(PathBuf::from(path)).is_some() {
|
||||
return Err("--config can only be specified once".into());
|
||||
}
|
||||
}
|
||||
"check-config" => {
|
||||
if !matches!(command, Command::Run) {
|
||||
return Err("only one command may be specified".into());
|
||||
}
|
||||
command = Command::CheckConfig;
|
||||
}
|
||||
"migrate" => {
|
||||
if !matches!(command, Command::Run) {
|
||||
return Err("only one command may be specified".into());
|
||||
}
|
||||
command = Command::Migrate;
|
||||
}
|
||||
_ if arg.starts_with('-') => return Err(format!("unknown option: {arg}; use --help").into()),
|
||||
_ => return Err(format!("unknown command: {arg}; use --help").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Cli { command, config })
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!(
|
||||
"rustpad {version}
|
||||
|
||||
USAGE:
|
||||
rustpad [OPTIONS] [COMMAND]
|
||||
|
||||
OPTIONS:
|
||||
-c, --config <FILE> Load YAML configuration file; environment variables override it
|
||||
-h, --help Show help
|
||||
-v, --version Show version
|
||||
|
||||
COMMANDS:
|
||||
check-config Parse and validate configuration, then exit
|
||||
migrate Validate configuration, apply database migrations, then exit",
|
||||
version = env!("CARGO_PKG_VERSION")
|
||||
);
|
||||
}
|
||||
|
||||
fn init_tracing() {
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
|
||||
Reference in New Issue
Block a user