Compare commits
2 commits
cd63f87f1b
...
e579a47e0f
Author | SHA1 | Date | |
---|---|---|---|
e579a47e0f | |||
3b75cfdbf8 |
5 changed files with 654 additions and 518 deletions
912
Cargo.lock
generated
912
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
43
Cargo.toml
43
Cargo.toml
|
@ -4,34 +4,31 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tower-service = "0.3.2"
|
||||
console_error_panic_hook = { version = "0.1.1" }
|
||||
anyhow = "1.0.91"
|
||||
serde_json = "1.0.132"
|
||||
oauth2 = "4.4.2"
|
||||
config = "0.14.1"
|
||||
dotenvy = "0.15.7"
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
reqwest = { version = "0.12.8", features = ["json"] }
|
||||
tracing = "0.1.40"
|
||||
askama = { version = "0.12.1", features = ["urlencode"] }
|
||||
async-session = "3.0.0"
|
||||
askama_axum = { version = "0.4.0", features = ["urlencode"] }
|
||||
askama = { version = "0.12.1", features = ["with-axum"] }
|
||||
futures = "0.3.31"
|
||||
uuid = { version = "1.11.0", features = ["js", "serde", "v4", "v7"] }
|
||||
rand = "0.8.5"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["chrono", "env-filter"] }
|
||||
tower-http = { version = "0.6.2", features = ["compression-gzip", "fs", "normalize-path", "trace"] }
|
||||
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "tracing"] }
|
||||
deadpool-diesel = { version = "0.6.1", features = ["postgres", "serde"] }
|
||||
axum = { version = "0.8.1", features = ["macros"] }
|
||||
axum-extra = { version = "0.10.0", features = ["cookie", "form", "typed-header"] }
|
||||
chrono = { version = "0.4.39", features = ["serde"] }
|
||||
base64 = "0.22.1"
|
||||
diesel = { version = "2.2.6", features = ["postgres", "chrono", "uuid", "serde_json"] }
|
||||
tower = "0.5.2"
|
||||
regex = "1.11.1"
|
||||
lettre = { version = "0.11.12", features = ["tokio1", "serde", "tracing", "tokio1-native-tls"] }
|
||||
chrono = { version = "0.4.39", features = ["serde"] }
|
||||
clap = { version = "4.5.31", features = ["derive"] }
|
||||
config = "0.14.1"
|
||||
deadpool-diesel = { version = "0.6.1", features = ["postgres", "serde"] }
|
||||
diesel = { version = "2.2.6", features = ["postgres", "chrono", "uuid", "serde_json"] }
|
||||
diesel_migrations = { version = "2.2.0", features = ["postgres"] }
|
||||
dotenvy = "0.15.7"
|
||||
futures = "0.3.31"
|
||||
lettre = { version = "0.11.12", features = ["tokio1", "serde", "tracing", "tokio1-native-tls"] }
|
||||
oauth2 = "4.4.2"
|
||||
rand = "0.8.5"
|
||||
regex = "1.11.1"
|
||||
reqwest = { version = "0.12.8", features = ["json"] }
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
tokio = { version = "1.42.0", features = ["full"] }
|
||||
tower = "0.5.2"
|
||||
tower-http = { version = "0.6.2", features = ["compression-gzip", "fs", "normalize-path", "trace"] }
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["chrono", "env-filter"] }
|
||||
uuid = { version = "1.11.0", features = ["serde", "v4", "v7"] }
|
||||
validator = { version = "0.20.0", features = ["derive"] }
|
||||
|
|
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
|
||||
}
|
||||
}
|
131
src/main.rs
131
src/main.rs
|
@ -1,66 +1,43 @@
|
|||
use app_state::App;
|
||||
use axum::middleware::map_request;
|
||||
use chrono::{TimeDelta, Utc};
|
||||
use clap::{Parser, Subcommand};
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use clap::Parser as _;
|
||||
use diesel_migrations::MigrationHarness;
|
||||
use dotenvy::dotenv;
|
||||
use middleware::lowercase_uri_path;
|
||||
use tokio::time::sleep;
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::{
|
||||
compression::CompressionLayer, normalize_path::NormalizePathLayer, trace::TraceLayer,
|
||||
};
|
||||
use migrations::MIGRATIONS;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::{app_state::AppState, router::new_router, settings::Settings, worker::run_worker};
|
||||
use crate::{
|
||||
app_state::{App, AppState},
|
||||
cli::{serve_command, worker_command, Cli, Commands},
|
||||
settings::Settings,
|
||||
};
|
||||
|
||||
pub mod api_keys;
|
||||
pub mod app_error;
|
||||
pub mod app_state;
|
||||
pub mod auth;
|
||||
pub mod channel_selections;
|
||||
pub mod channels;
|
||||
mod api_keys;
|
||||
mod app_error;
|
||||
mod app_state;
|
||||
mod auth;
|
||||
mod channel_selections;
|
||||
mod channels;
|
||||
mod channels_router;
|
||||
pub mod csrf;
|
||||
pub mod email;
|
||||
pub mod governors;
|
||||
pub mod guards;
|
||||
pub mod messages;
|
||||
pub mod middleware;
|
||||
mod cli;
|
||||
mod csrf;
|
||||
mod email;
|
||||
mod governors;
|
||||
mod guards;
|
||||
mod messages;
|
||||
mod middleware;
|
||||
mod migrations;
|
||||
mod nav_state;
|
||||
pub mod projects;
|
||||
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 router;
|
||||
mod schema;
|
||||
mod sessions;
|
||||
mod settings;
|
||||
mod team_memberships;
|
||||
mod teams;
|
||||
mod teams_router;
|
||||
pub mod users;
|
||||
mod users;
|
||||
mod v0_router;
|
||||
pub 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()
|
||||
}
|
||||
mod worker;
|
||||
|
||||
/// Run CLI
|
||||
#[tokio::main]
|
||||
|
@ -76,7 +53,6 @@ async fn main() {
|
|||
let state: AppState = App::from_settings(settings.clone()).await.unwrap().into();
|
||||
|
||||
if settings.run_database_migrations == Some(1) {
|
||||
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/");
|
||||
// Run migrations on server startup
|
||||
let conn = state.db_pool.get().await.unwrap();
|
||||
conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).map(|_| ()))
|
||||
|
@ -87,48 +63,7 @@ async fn main() {
|
|||
|
||||
let cli = Cli::parse();
|
||||
match &cli.command {
|
||||
Commands::Serve => {
|
||||
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((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();
|
||||
}
|
||||
}
|
||||
Commands::Serve => serve_command(state).await.unwrap(),
|
||||
Commands::Worker(args) => worker_command(args, 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