2022-09-03 10:35:15 +00:00
|
|
|
#![feature(let_chains)]
|
2022-09-05 01:48:43 +00:00
|
|
|
#![feature(drain_filter)]
|
2022-09-03 10:35:15 +00:00
|
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use anyhow::{Context, Result};
|
|
|
|
use sqlx::{Executor, Pool, Sqlite};
|
|
|
|
use sqlx::migrate::Migrator;
|
|
|
|
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
|
|
|
use tokio::sync::RwLock;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
2022-09-03 12:59:06 +00:00
|
|
|
use crate::config::Config;
|
2022-09-03 10:35:15 +00:00
|
|
|
use crate::pack::Pack;
|
|
|
|
|
|
|
|
mod pack;
|
|
|
|
mod message;
|
|
|
|
mod web;
|
|
|
|
mod util;
|
2022-09-03 12:59:06 +00:00
|
|
|
mod config;
|
2022-09-09 10:10:15 +00:00
|
|
|
mod consts;
|
2022-09-03 10:35:15 +00:00
|
|
|
|
|
|
|
static MIGRATOR: Migrator = sqlx::migrate!();
|
|
|
|
|
|
|
|
pub struct State {
|
2022-09-03 12:59:06 +00:00
|
|
|
pub config: Config,
|
2022-09-03 10:35:15 +00:00
|
|
|
pub db: Pool<Sqlite>,
|
|
|
|
pub packs: RwLock<HashMap<Uuid, Pack>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl State {
|
|
|
|
pub async fn update_packs(&self) -> Result<()> {
|
|
|
|
let mut packs = HashMap::new();
|
|
|
|
|
2022-09-03 12:59:06 +00:00
|
|
|
let mut dir = tokio::fs::read_dir(&self.config.packs).await?;
|
2022-09-03 10:35:15 +00:00
|
|
|
while let Ok(Some(entry)) = dir.next_entry().await {
|
|
|
|
if !entry.path().is_file() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
match entry.path().extension().and_then(|x| x.to_str()) {
|
|
|
|
Some("yaml") | Some("yml") => {}
|
|
|
|
_ => continue,
|
|
|
|
}
|
|
|
|
|
|
|
|
let text = match tokio::fs::read_to_string(entry.path()).await {
|
|
|
|
Ok(t) => t,
|
|
|
|
Err(e) => {
|
|
|
|
eprintln!("error reading pack: {:#?}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match serde_yaml::from_str::<Pack>(&text) {
|
|
|
|
Ok(pack) => {
|
|
|
|
println!("added {}", pack.name);
|
|
|
|
packs.insert(pack.id, pack);
|
|
|
|
}
|
|
|
|
Err(e) => eprintln!("error parsing pack at {:?}: {:#?}", entry.path(), e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*self.packs.write().await = packs;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> Result<()> {
|
2022-09-03 12:59:06 +00:00
|
|
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
|
|
|
if args.is_empty() {
|
|
|
|
eprintln!("usage: server [config]");
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let config_str = tokio::fs::read_to_string(&args[0])
|
|
|
|
.await
|
|
|
|
.with_context(|| format!("could not read config file at {}", args[0]))?;
|
|
|
|
let config: Config = toml::from_str(&config_str)
|
|
|
|
.context("could not parse config file")?;
|
|
|
|
|
2022-09-03 10:35:15 +00:00
|
|
|
let options = SqliteConnectOptions::new();
|
|
|
|
// options.log_statements(LevelFilter::Debug);
|
|
|
|
|
|
|
|
let pool = SqlitePoolOptions::new()
|
|
|
|
.after_connect(|conn, _| Box::pin(async move {
|
|
|
|
conn.execute(
|
|
|
|
// language=sqlite
|
|
|
|
"PRAGMA foreign_keys = ON;"
|
|
|
|
).await?;
|
|
|
|
Ok(())
|
|
|
|
}))
|
2022-09-03 12:59:06 +00:00
|
|
|
.connect_with(options.filename(&config.database))
|
2022-09-03 10:35:15 +00:00
|
|
|
.await
|
|
|
|
.context("could not connect to database")?;
|
|
|
|
MIGRATOR.run(&pool)
|
|
|
|
.await
|
|
|
|
.context("could not run database migrations")?;
|
|
|
|
|
|
|
|
let state = Arc::new(State {
|
2022-09-03 12:59:06 +00:00
|
|
|
config,
|
2022-09-03 10:35:15 +00:00
|
|
|
db: pool,
|
|
|
|
packs: Default::default(),
|
|
|
|
});
|
|
|
|
|
2022-09-03 13:55:36 +00:00
|
|
|
println!("adding packs");
|
2022-09-03 10:35:15 +00:00
|
|
|
state.update_packs().await?;
|
|
|
|
|
2022-09-05 08:23:10 +00:00
|
|
|
let address = state.config.address;
|
2022-09-03 13:55:36 +00:00
|
|
|
println!("listening at {}", address);
|
2022-09-03 12:59:06 +00:00
|
|
|
warp::serve(web::routes(state)).run(address).await;
|
2022-09-03 10:35:15 +00:00
|
|
|
Ok(())
|
|
|
|
}
|