2025-03-14 13:04:57 -07:00
|
|
|
use app_state::App;
|
|
|
|
use axum::middleware::map_request;
|
2025-03-08 22:18:24 -08:00
|
|
|
use chrono::{TimeDelta, Utc};
|
|
|
|
use clap::{Parser, Subcommand};
|
2025-03-11 10:33:37 -07:00
|
|
|
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
2025-03-14 13:04:57 -07:00
|
|
|
use dotenvy::dotenv;
|
|
|
|
use middleware::lowercase_uri_path;
|
2025-03-08 22:18:24 -08:00
|
|
|
use tokio::time::sleep;
|
2025-03-11 22:22:30 -07:00
|
|
|
use tower::ServiceBuilder;
|
|
|
|
use tower_http::{
|
|
|
|
compression::CompressionLayer, normalize_path::NormalizePathLayer, trace::TraceLayer,
|
|
|
|
};
|
2025-02-26 13:10:50 -08:00
|
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
|
2025-03-14 13:04:57 -07:00
|
|
|
use crate::{app_state::AppState, router::new_router, settings::Settings, worker::run_worker};
|
|
|
|
|
|
|
|
pub mod api_keys;
|
|
|
|
pub mod app_error;
|
|
|
|
pub mod app_state;
|
|
|
|
pub mod auth;
|
|
|
|
pub mod channel_selections;
|
|
|
|
pub mod channels;
|
|
|
|
mod channels_router;
|
|
|
|
pub mod csrf;
|
|
|
|
pub mod email;
|
|
|
|
pub mod governors;
|
|
|
|
pub mod guards;
|
|
|
|
pub mod messages;
|
|
|
|
pub mod middleware;
|
|
|
|
mod nav_state;
|
|
|
|
pub mod projects;
|
|
|
|
mod projects_router;
|
|
|
|
pub mod router;
|
|
|
|
pub mod schema;
|
|
|
|
pub mod sessions;
|
|
|
|
pub mod settings;
|
|
|
|
pub mod team_memberships;
|
|
|
|
pub mod teams;
|
|
|
|
mod teams_router;
|
|
|
|
pub mod users;
|
|
|
|
mod v0_router;
|
|
|
|
pub mod worker;
|
2025-02-26 13:10:50 -08:00
|
|
|
|
2025-03-08 22:18:24 -08:00
|
|
|
#[derive(Parser)]
|
|
|
|
#[command(version, about, long_about = None)]
|
|
|
|
struct Cli {
|
|
|
|
#[command(subcommand)]
|
|
|
|
command: Commands,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Subcommand)]
|
|
|
|
enum Commands {
|
|
|
|
/// Run web server
|
|
|
|
Serve,
|
|
|
|
/// Run background worker
|
|
|
|
Worker {
|
|
|
|
/// Loop the every n seconds instead of exiting after execution
|
|
|
|
#[arg(long)]
|
|
|
|
auto_loop_seconds: Option<u32>,
|
|
|
|
},
|
2025-03-10 14:52:02 -07:00
|
|
|
// TODO: add a low-frequency worker task exclusively for self-healing
|
|
|
|
// mechanisms like Governor::reset_all()
|
2025-03-08 22:18:24 -08:00
|
|
|
}
|
|
|
|
|
2025-03-14 13:04:57 -07:00
|
|
|
/// Run CLI
|
2025-02-26 13:10:50 -08:00
|
|
|
#[tokio::main]
|
|
|
|
async fn main() {
|
2025-03-14 13:04:57 -07:00
|
|
|
// Attempt to pre-load .env in case it contains a RUST_LOG variable
|
|
|
|
dotenv().ok();
|
2025-02-26 13:10:50 -08:00
|
|
|
tracing_subscriber::fmt()
|
|
|
|
.with_env_filter(EnvFilter::from_default_env())
|
|
|
|
.init();
|
|
|
|
|
2025-03-11 10:29:22 -07:00
|
|
|
let settings = Settings::load().unwrap();
|
|
|
|
|
2025-03-14 13:04:57 -07:00
|
|
|
let state: AppState = App::from_settings(settings.clone()).await.unwrap().into();
|
2025-02-26 13:10:43 -08:00
|
|
|
|
2025-03-11 10:33:37 -07:00
|
|
|
if settings.run_database_migrations == Some(1) {
|
|
|
|
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/");
|
|
|
|
// Run migrations on server startup
|
2025-03-14 13:04:57 -07:00
|
|
|
let conn = state.db_pool.get().await.unwrap();
|
2025-03-11 10:33:37 -07:00
|
|
|
conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).map(|_| ()))
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
2025-03-14 13:04:57 -07:00
|
|
|
let cli = Cli::parse();
|
2025-03-08 22:18:24 -08:00
|
|
|
match &cli.command {
|
|
|
|
Commands::Serve => {
|
2025-03-14 13:04:57 -07:00
|
|
|
let router = new_router(state.clone()).layer(
|
|
|
|
ServiceBuilder::new()
|
|
|
|
.layer(map_request(lowercase_uri_path))
|
|
|
|
.layer(TraceLayer::new_for_http())
|
|
|
|
.layer(CompressionLayer::new())
|
|
|
|
.layer(NormalizePathLayer::trim_trailing_slash()),
|
|
|
|
);
|
2025-02-26 13:10:50 -08:00
|
|
|
|
2025-03-14 13:04:57 -07:00
|
|
|
let listener = tokio::net::TcpListener::bind((settings.host.clone(), settings.port))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2025-03-08 22:18:24 -08:00
|
|
|
tracing::info!(
|
|
|
|
"App running at http://{}:{}{}",
|
|
|
|
settings.host,
|
|
|
|
settings.port,
|
|
|
|
settings.base_path
|
|
|
|
);
|
2025-03-11 22:22:30 -07:00
|
|
|
|
2025-03-14 13:04:57 -07:00
|
|
|
axum::serve(listener, router).await.unwrap();
|
2025-03-08 22:18:24 -08:00
|
|
|
}
|
|
|
|
Commands::Worker { auto_loop_seconds } => {
|
|
|
|
if let Some(loop_seconds) = auto_loop_seconds {
|
|
|
|
let loop_delta = TimeDelta::seconds(i64::from(*loop_seconds));
|
|
|
|
loop {
|
|
|
|
let t_next_loop = Utc::now() + loop_delta;
|
|
|
|
|
2025-03-14 13:04:57 -07:00
|
|
|
if let Err(err) = run_worker(state.clone()).await {
|
2025-03-08 22:18:24 -08:00
|
|
|
tracing::error!("{}", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
let sleep_delta = t_next_loop - Utc::now();
|
|
|
|
match sleep_delta.to_std() {
|
|
|
|
Ok(duration) => {
|
|
|
|
sleep(duration).await;
|
|
|
|
}
|
|
|
|
Err(_) => { /* sleep_delta was < 0, so don't sleep */ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2025-03-14 13:04:57 -07:00
|
|
|
run_worker(state).await.unwrap();
|
2025-03-08 22:18:24 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-02-26 13:10:50 -08:00
|
|
|
}
|