Compare commits
1 commit
82eeead643
...
e579a47e0f
Author | SHA1 | Date | |
---|---|---|---|
e579a47e0f |
3 changed files with 115 additions and 98 deletions
83
src/cli.rs
Normal file
83
src/cli.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use axum::middleware::map_request;
|
||||||
|
use chrono::{TimeDelta, Utc};
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
use tokio::time::sleep;
|
||||||
|
use tower::ServiceBuilder;
|
||||||
|
use tower_http::{
|
||||||
|
compression::CompressionLayer, normalize_path::NormalizePathLayer, trace::TraceLayer,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app_state::AppState, middleware::lowercase_uri_path, router::new_router, worker::run_worker,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
pub struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct WorkerArgs {
|
||||||
|
/// Loop the every n seconds instead of exiting after execution
|
||||||
|
#[arg(long)]
|
||||||
|
auto_loop_seconds: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Commands {
|
||||||
|
/// Run web server
|
||||||
|
Serve,
|
||||||
|
/// Run background worker
|
||||||
|
Worker(WorkerArgs),
|
||||||
|
// TODO: add a low-frequency worker task exclusively for self-healing
|
||||||
|
// mechanisms like Governor::reset_all()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn serve_command(state: AppState) -> Result<()> {
|
||||||
|
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()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let listener =
|
||||||
|
tokio::net::TcpListener::bind((state.settings.host.clone(), state.settings.port))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
tracing::info!(
|
||||||
|
"App running at http://{}:{}{}",
|
||||||
|
state.settings.host,
|
||||||
|
state.settings.port,
|
||||||
|
state.settings.base_path
|
||||||
|
);
|
||||||
|
|
||||||
|
axum::serve(listener, router).await.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn worker_command(args: &WorkerArgs, state: AppState) -> Result<()> {
|
||||||
|
if let Some(loop_seconds) = args.auto_loop_seconds {
|
||||||
|
let loop_delta = TimeDelta::seconds(i64::from(loop_seconds));
|
||||||
|
loop {
|
||||||
|
let t_next_loop = Utc::now() + loop_delta;
|
||||||
|
|
||||||
|
if let Err(err) = run_worker(state.clone()).await {
|
||||||
|
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 {
|
||||||
|
run_worker(state).await
|
||||||
|
}
|
||||||
|
}
|
127
src/main.rs
127
src/main.rs
|
@ -1,70 +1,43 @@
|
||||||
use axum::middleware::map_request;
|
use clap::Parser as _;
|
||||||
use chrono::{TimeDelta, Utc};
|
use diesel_migrations::MigrationHarness;
|
||||||
use clap::{Parser, Subcommand};
|
|
||||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use tokio::time::sleep;
|
use migrations::MIGRATIONS;
|
||||||
use tower::ServiceBuilder;
|
|
||||||
use tower_http::{
|
|
||||||
compression::CompressionLayer, normalize_path::NormalizePathLayer, trace::TraceLayer,
|
|
||||||
};
|
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app_state::{App, AppState},
|
app_state::{App, AppState},
|
||||||
middleware::lowercase_uri_path,
|
cli::{serve_command, worker_command, Cli, Commands},
|
||||||
router::new_router,
|
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
worker::run_worker,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod api_keys;
|
mod api_keys;
|
||||||
pub mod app_error;
|
mod app_error;
|
||||||
pub mod app_state;
|
mod app_state;
|
||||||
pub mod auth;
|
mod auth;
|
||||||
pub mod channel_selections;
|
mod channel_selections;
|
||||||
pub mod channels;
|
mod channels;
|
||||||
mod channels_router;
|
mod channels_router;
|
||||||
pub mod csrf;
|
mod cli;
|
||||||
pub mod email;
|
mod csrf;
|
||||||
pub mod governors;
|
mod email;
|
||||||
pub mod guards;
|
mod governors;
|
||||||
pub mod messages;
|
mod guards;
|
||||||
pub mod middleware;
|
mod messages;
|
||||||
|
mod middleware;
|
||||||
|
mod migrations;
|
||||||
mod nav_state;
|
mod nav_state;
|
||||||
pub mod projects;
|
mod projects;
|
||||||
mod projects_router;
|
mod projects_router;
|
||||||
pub mod router;
|
mod router;
|
||||||
pub mod schema;
|
mod schema;
|
||||||
pub mod sessions;
|
mod sessions;
|
||||||
pub mod settings;
|
mod settings;
|
||||||
pub mod team_memberships;
|
mod team_memberships;
|
||||||
pub mod teams;
|
mod teams;
|
||||||
mod teams_router;
|
mod teams_router;
|
||||||
pub mod users;
|
mod users;
|
||||||
mod v0_router;
|
mod v0_router;
|
||||||
pub mod worker;
|
mod worker;
|
||||||
|
|
||||||
#[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>,
|
|
||||||
},
|
|
||||||
// TODO: add a low-frequency worker task exclusively for self-healing
|
|
||||||
// mechanisms like Governor::reset_all()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run CLI
|
/// Run CLI
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -80,7 +53,6 @@ async fn main() {
|
||||||
let state: AppState = App::from_settings(settings.clone()).await.unwrap().into();
|
let state: AppState = App::from_settings(settings.clone()).await.unwrap().into();
|
||||||
|
|
||||||
if settings.run_database_migrations == Some(1) {
|
if settings.run_database_migrations == Some(1) {
|
||||||
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/");
|
|
||||||
// Run migrations on server startup
|
// Run migrations on server startup
|
||||||
let conn = state.db_pool.get().await.unwrap();
|
let conn = state.db_pool.get().await.unwrap();
|
||||||
conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).map(|_| ()))
|
conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).map(|_| ()))
|
||||||
|
@ -91,48 +63,7 @@ async fn main() {
|
||||||
|
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Commands::Serve => {
|
Commands::Serve => serve_command(state).await.unwrap(),
|
||||||
let router = new_router(state.clone()).layer(
|
Commands::Worker(args) => worker_command(args, state).await.unwrap(),
|
||||||
ServiceBuilder::new()
|
|
||||||
.layer(map_request(lowercase_uri_path))
|
|
||||||
.layer(TraceLayer::new_for_http())
|
|
||||||
.layer(CompressionLayer::new())
|
|
||||||
.layer(NormalizePathLayer::trim_trailing_slash()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind((settings.host.clone(), settings.port))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
tracing::info!(
|
|
||||||
"App running at http://{}:{}{}",
|
|
||||||
settings.host,
|
|
||||||
settings.port,
|
|
||||||
settings.base_path
|
|
||||||
);
|
|
||||||
|
|
||||||
axum::serve(listener, router).await.unwrap();
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
if let Err(err) = run_worker(state.clone()).await {
|
|
||||||
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 {
|
|
||||||
run_worker(state).await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
src/migrations.rs
Normal file
3
src/migrations.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
use diesel_migrations::{embed_migrations, EmbeddedMigrations};
|
||||||
|
|
||||||
|
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/");
|
Loading…
Add table
Reference in a new issue