Restructure server around Server struct
This commit is contained in:
parent
45abda2b6d
commit
9bdfc79c8b
5 changed files with 92 additions and 95 deletions
|
|
@ -2,14 +2,12 @@ mod args;
|
||||||
mod config;
|
mod config;
|
||||||
mod server;
|
mod server;
|
||||||
mod somehow;
|
mod somehow;
|
||||||
mod state;
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use std::{io, path::PathBuf, process};
|
use std::{io, path::PathBuf, process};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use state::AppState;
|
|
||||||
use tokio::{select, signal::unix::SignalKind};
|
use tokio::{select, signal::unix::SignalKind};
|
||||||
use tracing::{debug, error, info, Level};
|
use tracing::{debug, error, info, Level};
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
|
|
@ -19,6 +17,7 @@ use tracing_subscriber::{
|
||||||
use crate::{
|
use crate::{
|
||||||
args::{Args, NAME, VERSION},
|
args::{Args, NAME, VERSION},
|
||||||
config::Config,
|
config::Config,
|
||||||
|
server::Server,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn set_up_logging(verbose: u8) {
|
fn set_up_logging(verbose: u8) {
|
||||||
|
|
@ -99,12 +98,12 @@ async fn run() -> somehow::Result<()> {
|
||||||
info!("You are running {NAME} {VERSION}");
|
info!("You are running {NAME} {VERSION}");
|
||||||
|
|
||||||
let config = load_config(args.config)?;
|
let config = load_config(args.config)?;
|
||||||
let state = AppState::new(config, &args.db, &args.repo).await?;
|
let server = Server::new(config, &args.db, &args.repo).await?;
|
||||||
|
|
||||||
info!("Startup complete, running");
|
info!("Startup complete, running");
|
||||||
select! {
|
select! {
|
||||||
_ = wait_for_signal() => {}
|
_ = wait_for_signal() => {}
|
||||||
_ = server::run(state.clone()) => {}
|
_ = server.run() => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
select! {
|
select! {
|
||||||
|
|
@ -118,7 +117,7 @@ async fn run() -> somehow::Result<()> {
|
||||||
// In order to fix this, I could maybe register a bare signal handler
|
// In order to fix this, I could maybe register a bare signal handler
|
||||||
// (instead of using tokio streams) that just calls process::exit(1) and
|
// (instead of using tokio streams) that just calls process::exit(1) and
|
||||||
// nothing else?
|
// nothing else?
|
||||||
_ = state.shut_down() => {}
|
_ = server.shut_down() => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,87 @@
|
||||||
mod recurring;
|
mod recurring;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
|
use std::{path::Path, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use axum::extract::FromRef;
|
||||||
|
use gix::ThreadSafeRepository;
|
||||||
|
use sqlx::{
|
||||||
|
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions, SqliteSynchronous},
|
||||||
|
SqlitePool,
|
||||||
|
};
|
||||||
use tokio::select;
|
use tokio::select;
|
||||||
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::{somehow, state::AppState};
|
use crate::{config::Config, somehow};
|
||||||
|
|
||||||
pub async fn run(state: AppState) -> somehow::Result<()> {
|
async fn open_db(db_path: &Path) -> sqlx::Result<SqlitePool> {
|
||||||
select! {
|
let options = SqliteConnectOptions::new()
|
||||||
e = web::run(state.clone()) => e,
|
// https://www.sqlite.org/pragma.html#pragma_journal_mode
|
||||||
() = recurring::run(state.clone()) => Ok(()),
|
.journal_mode(SqliteJournalMode::Wal)
|
||||||
|
// https://www.sqlite.org/pragma.html#pragma_synchronous
|
||||||
|
// NORMAL recommended when using WAL, can't cause corruption
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
// https://www.sqlite.org/pragma.html#pragma_foreign_keys
|
||||||
|
.foreign_keys(true)
|
||||||
|
// https://www.sqlite.org/pragma.html#pragma_trusted_schema
|
||||||
|
// The docs recommend always turning this off
|
||||||
|
.pragma("trusted_schema", "false")
|
||||||
|
.filename(db_path)
|
||||||
|
.create_if_missing(true)
|
||||||
|
// https://www.sqlite.org/lang_analyze.html#recommended_usage_pattern
|
||||||
|
// https://www.sqlite.org/pragma.html#pragma_analysis_limit
|
||||||
|
// https://www.sqlite.org/pragma.html#pragma_optimize
|
||||||
|
.optimize_on_close(true, Some(1000));
|
||||||
|
|
||||||
|
info!(path = %db_path.display(), "Opening db");
|
||||||
|
let pool = SqlitePoolOptions::new()
|
||||||
|
// Regularly optimize the db as recommended by the sqlite docs
|
||||||
|
// https://www.sqlite.org/lang_analyze.html#recommended_usage_pattern
|
||||||
|
// https://github.com/launchbadge/sqlx/issues/2111#issuecomment-1254394698
|
||||||
|
.max_lifetime(Some(Duration::from_secs(60 * 60 * 24)))
|
||||||
|
.connect_with(options)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
debug!("Applying outstanding db migrations");
|
||||||
|
sqlx::migrate!().run(&pool).await?;
|
||||||
|
|
||||||
|
Ok(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_repo(repo_path: &Path) -> somehow::Result<ThreadSafeRepository> {
|
||||||
|
info!(path = %repo_path.display(), "Opening repo");
|
||||||
|
Ok(ThreadSafeRepository::open(repo_path)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, FromRef)]
|
||||||
|
pub struct Server {
|
||||||
|
pub config: &'static Config,
|
||||||
|
pub db: SqlitePool,
|
||||||
|
pub repo: Arc<ThreadSafeRepository>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Server {
|
||||||
|
pub async fn new(
|
||||||
|
config: &'static Config,
|
||||||
|
db_path: &Path,
|
||||||
|
repo_path: &Path,
|
||||||
|
) -> somehow::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
config,
|
||||||
|
db: open_db(db_path).await?,
|
||||||
|
repo: Arc::new(open_repo(repo_path)?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&self) -> somehow::Result<()> {
|
||||||
|
select! {
|
||||||
|
e = web::run(self.clone()) => e,
|
||||||
|
() = recurring::run(self.clone()) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn shut_down(self) {
|
||||||
|
info!("Closing db");
|
||||||
|
self.db.close().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ mod repo;
|
||||||
|
|
||||||
use tracing::{debug_span, error, Instrument};
|
use tracing::{debug_span, error, Instrument};
|
||||||
|
|
||||||
use crate::state::AppState;
|
use super::Server;
|
||||||
|
|
||||||
async fn recurring_task(state: &AppState) {
|
async fn recurring_task(state: &Server) {
|
||||||
async {
|
async {
|
||||||
if let Err(e) = repo::update(&state.db, state.repo.clone()).await {
|
if let Err(e) = repo::update(&state.db, state.repo.clone()).await {
|
||||||
error!("Error updating repo:\n{e:?}");
|
error!("Error updating repo:\n{e:?}");
|
||||||
|
|
@ -28,7 +28,7 @@ async fn recurring_task(state: &AppState) {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(state: AppState) {
|
pub async fn run(state: Server) {
|
||||||
loop {
|
loop {
|
||||||
recurring_task(&state).await;
|
recurring_task(&state).await;
|
||||||
tokio::time::sleep(state.config.repo.update_delay).await;
|
tokio::time::sleep(state.config.repo.update_delay).await;
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ mod queue;
|
||||||
mod queue_id;
|
mod queue_id;
|
||||||
mod r#static;
|
mod r#static;
|
||||||
|
|
||||||
use axum::{routing::get, Router, Server};
|
use axum::{routing::get, Router};
|
||||||
|
|
||||||
use crate::{config::Config, somehow, state::AppState};
|
use crate::{config::Config, somehow};
|
||||||
|
|
||||||
|
use super::Server;
|
||||||
|
|
||||||
pub enum Tab {
|
pub enum Tab {
|
||||||
Index,
|
Index,
|
||||||
|
|
@ -37,7 +39,7 @@ impl Base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(state: AppState) -> somehow::Result<()> {
|
pub async fn run(state: Server) -> somehow::Result<()> {
|
||||||
// TODO Add text body to body-less status codes
|
// TODO Add text body to body-less status codes
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
|
|
@ -50,7 +52,7 @@ pub async fn run(state: AppState) -> somehow::Result<()> {
|
||||||
.fallback(get(r#static::static_handler))
|
.fallback(get(r#static::static_handler))
|
||||||
.with_state(state.clone());
|
.with_state(state.clone());
|
||||||
|
|
||||||
Server::bind(&"0.0.0.0:8000".parse().unwrap())
|
axum::Server::bind(&"0.0.0.0:8000".parse().unwrap())
|
||||||
.serve(app.into_make_service())
|
.serve(app.into_make_service())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
||||||
78
src/state.rs
78
src/state.rs
|
|
@ -1,78 +0,0 @@
|
||||||
//! Globally accessible application state.
|
|
||||||
|
|
||||||
use std::{path::Path, sync::Arc, time::Duration};
|
|
||||||
|
|
||||||
use axum::extract::FromRef;
|
|
||||||
use gix::ThreadSafeRepository;
|
|
||||||
use sqlx::{
|
|
||||||
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions, SqliteSynchronous},
|
|
||||||
SqlitePool,
|
|
||||||
};
|
|
||||||
use tracing::{debug, info};
|
|
||||||
|
|
||||||
use crate::{config::Config, somehow};
|
|
||||||
|
|
||||||
async fn open_db(db_path: &Path) -> sqlx::Result<SqlitePool> {
|
|
||||||
let options = SqliteConnectOptions::new()
|
|
||||||
// https://www.sqlite.org/pragma.html#pragma_journal_mode
|
|
||||||
.journal_mode(SqliteJournalMode::Wal)
|
|
||||||
// https://www.sqlite.org/pragma.html#pragma_synchronous
|
|
||||||
// NORMAL recommended when using WAL, can't cause corruption
|
|
||||||
.synchronous(SqliteSynchronous::Normal)
|
|
||||||
// https://www.sqlite.org/pragma.html#pragma_foreign_keys
|
|
||||||
.foreign_keys(true)
|
|
||||||
// https://www.sqlite.org/pragma.html#pragma_trusted_schema
|
|
||||||
// The docs recommend always turning this off
|
|
||||||
.pragma("trusted_schema", "false")
|
|
||||||
.filename(db_path)
|
|
||||||
.create_if_missing(true)
|
|
||||||
// https://www.sqlite.org/lang_analyze.html#recommended_usage_pattern
|
|
||||||
// https://www.sqlite.org/pragma.html#pragma_analysis_limit
|
|
||||||
// https://www.sqlite.org/pragma.html#pragma_optimize
|
|
||||||
.optimize_on_close(true, Some(1000));
|
|
||||||
|
|
||||||
info!(path = %db_path.display(), "Opening db");
|
|
||||||
let pool = SqlitePoolOptions::new()
|
|
||||||
// Regularly optimize the db as recommended by the sqlite docs
|
|
||||||
// https://www.sqlite.org/lang_analyze.html#recommended_usage_pattern
|
|
||||||
// https://github.com/launchbadge/sqlx/issues/2111#issuecomment-1254394698
|
|
||||||
.max_lifetime(Some(Duration::from_secs(60 * 60 * 24)))
|
|
||||||
.connect_with(options)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
debug!("Applying outstanding db migrations");
|
|
||||||
sqlx::migrate!().run(&pool).await?;
|
|
||||||
|
|
||||||
Ok(pool)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_repo(repo_path: &Path) -> somehow::Result<ThreadSafeRepository> {
|
|
||||||
info!(path = %repo_path.display(), "Opening repo");
|
|
||||||
Ok(ThreadSafeRepository::open(repo_path)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, FromRef)]
|
|
||||||
pub struct AppState {
|
|
||||||
pub config: &'static Config,
|
|
||||||
pub db: SqlitePool,
|
|
||||||
pub repo: Arc<ThreadSafeRepository>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppState {
|
|
||||||
pub async fn new(
|
|
||||||
config: &'static Config,
|
|
||||||
db_path: &Path,
|
|
||||||
repo_path: &Path,
|
|
||||||
) -> somehow::Result<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
config,
|
|
||||||
db: open_db(db_path).await?,
|
|
||||||
repo: Arc::new(open_repo(repo_path)?),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn shut_down(self) {
|
|
||||||
info!("Closing db");
|
|
||||||
self.db.close().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue