|
|
@ -7,18 +7,19 @@ extern crate thiserror; |
|
|
|
|
|
|
|
|
|
|
|
use std::collections::HashMap; |
|
|
|
use std::collections::HashMap; |
|
|
|
use std::ops::Deref; |
|
|
|
use std::ops::Deref; |
|
|
|
|
|
|
|
use std::path::PathBuf; |
|
|
|
|
|
|
|
|
|
|
|
use actix_session::CookieSession; |
|
|
|
use actix_session::CookieSession; |
|
|
|
use actix_web::{web, App, HttpResponse, HttpServer}; |
|
|
|
use actix_web::{App, HttpResponse, HttpServer, web}; |
|
|
|
use actix_web_static_files; |
|
|
|
use actix_web_static_files; |
|
|
|
use actix_web_static_files::ResourceFiles as StaticFiles; |
|
|
|
use actix_web_static_files::ResourceFiles as StaticFiles; |
|
|
|
|
|
|
|
use clap::Arg; |
|
|
|
use log::LevelFilter; |
|
|
|
use log::LevelFilter; |
|
|
|
use once_cell::sync::Lazy; |
|
|
|
use once_cell::sync::Lazy; |
|
|
|
use rand::Rng; |
|
|
|
use rand::Rng; |
|
|
|
use tera::Tera; |
|
|
|
use tera::Tera; |
|
|
|
|
|
|
|
|
|
|
|
use yopa::insert::{InsertObj, InsertRel, InsertValue}; |
|
|
|
use yopa::{Storage}; |
|
|
|
use yopa::{Storage, TypedValue}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use crate::tera_ext::TeraExt; |
|
|
|
use crate::tera_ext::TeraExt; |
|
|
|
|
|
|
|
|
|
|
@ -101,25 +102,83 @@ pub(crate) static TERA: Lazy<Tera> = Lazy::new(|| { |
|
|
|
|
|
|
|
|
|
|
|
type YopaStoreWrapper = web::Data<tokio::sync::RwLock<yopa::Storage>>; |
|
|
|
type YopaStoreWrapper = web::Data<tokio::sync::RwLock<yopa::Storage>>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const DEF_ADDRESS : &str = "127.0.0.1:8080"; |
|
|
|
|
|
|
|
|
|
|
|
#[actix_web::main] |
|
|
|
#[actix_web::main] |
|
|
|
async fn main() -> std::io::Result<()> { |
|
|
|
async fn main() -> std::io::Result<()> { |
|
|
|
simple_logging::log_to_stderr(LevelFilter::Debug); |
|
|
|
let def_addr_help = format!("Set custom server address, default {}", DEF_ADDRESS); |
|
|
|
|
|
|
|
let app = clap::App::new("yopa") |
|
|
|
|
|
|
|
.arg(Arg::with_name("version") |
|
|
|
|
|
|
|
.long("version") |
|
|
|
|
|
|
|
.short("V") |
|
|
|
|
|
|
|
.help("Show version and exit")) |
|
|
|
|
|
|
|
.arg(Arg::with_name("verbose") |
|
|
|
|
|
|
|
.short("v") |
|
|
|
|
|
|
|
.multiple(true) |
|
|
|
|
|
|
|
.help("Increase verbosity of logging")) |
|
|
|
|
|
|
|
.arg(Arg::with_name("address") |
|
|
|
|
|
|
|
.short("a") |
|
|
|
|
|
|
|
.takes_value(true) |
|
|
|
|
|
|
|
.help(&def_addr_help)) |
|
|
|
|
|
|
|
.arg(Arg::with_name("json") |
|
|
|
|
|
|
|
.long("json") |
|
|
|
|
|
|
|
.short("j") |
|
|
|
|
|
|
|
.help("Use JSON storage format instead of binary")) |
|
|
|
|
|
|
|
.arg(Arg::with_name("file") |
|
|
|
|
|
|
|
.help("Database file to use")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let matches = app.get_matches(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if matches.is_present("version") { |
|
|
|
|
|
|
|
println!("yopa-web {}, using yopa {}", env!("CARGO_PKG_VERSION"), yopa::VERSION); |
|
|
|
|
|
|
|
return Ok(()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
simple_logging::log_to_stderr(match matches.occurrences_of("verbose") { |
|
|
|
|
|
|
|
0 => LevelFilter::Warn, |
|
|
|
|
|
|
|
1 => LevelFilter::Info, |
|
|
|
|
|
|
|
2 => LevelFilter::Debug, |
|
|
|
|
|
|
|
_ => LevelFilter::Trace, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debug!("Load Tera templates"); |
|
|
|
// Ensure the lazy ref is initialized early (to catch template bugs ASAP)
|
|
|
|
// Ensure the lazy ref is initialized early (to catch template bugs ASAP)
|
|
|
|
let _ = TERA.deref(); |
|
|
|
let _ = TERA.deref(); |
|
|
|
|
|
|
|
|
|
|
|
let yopa_store: YopaStoreWrapper = init_yopa() |
|
|
|
let file = matches.value_of("file").unwrap_or("yopa-store.dat"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let file_path = if file.starts_with('/') { |
|
|
|
|
|
|
|
std::env::current_dir()?.join(file) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
PathBuf::from(file) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debug!("Using database file: {}", file_path.display()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut store = if matches.is_present("json") { |
|
|
|
|
|
|
|
Storage::new_json(file_path) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
Storage::new_bincode(file_path) |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
store.load() |
|
|
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; |
|
|
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let yopa_store: YopaStoreWrapper = web::Data::new(tokio::sync::RwLock::new(store)); |
|
|
|
|
|
|
|
|
|
|
|
let mut session_key = [0u8; 32]; |
|
|
|
let mut session_key = [0u8; 32]; |
|
|
|
rand::thread_rng().fill(&mut session_key); |
|
|
|
rand::thread_rng().fill(&mut session_key); |
|
|
|
|
|
|
|
trace!("Cookie session key: {:?}", session_key); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let server_address = matches.value_of("address").unwrap_or(DEF_ADDRESS); |
|
|
|
|
|
|
|
|
|
|
|
debug!("Session key: {:?}", session_key); |
|
|
|
println!("Starting web interface at http://{}", server_address); |
|
|
|
|
|
|
|
|
|
|
|
HttpServer::new(move || { |
|
|
|
HttpServer::new(move || { |
|
|
|
let static_files = |
|
|
|
let static_files = |
|
|
|
StaticFiles::new("/static", included_static_files()).do_not_resolve_defaults(); |
|
|
|
StaticFiles::new("/static", included_static_files()).do_not_resolve_defaults(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debug!("Starting server thread"); |
|
|
|
App::new() |
|
|
|
App::new() |
|
|
|
/* Middlewares */ |
|
|
|
/* Middlewares */ |
|
|
|
.wrap(CookieSession::signed(&session_key).secure(false)) |
|
|
|
.wrap(CookieSession::signed(&session_key).secure(false)) |
|
|
@ -162,14 +221,7 @@ async fn main() -> std::io::Result<()> { |
|
|
|
HttpResponse::NotFound().body("File or endpoint not found") |
|
|
|
HttpResponse::NotFound().body("File or endpoint not found") |
|
|
|
})) |
|
|
|
})) |
|
|
|
}) |
|
|
|
}) |
|
|
|
.bind("127.0.0.1:8080")? |
|
|
|
.bind(server_address)? |
|
|
|
.run() |
|
|
|
.run() |
|
|
|
.await |
|
|
|
.await |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn init_yopa() -> anyhow::Result<YopaStoreWrapper> { |
|
|
|
|
|
|
|
let mut store = Storage::new_bincode(std::env::current_dir()?.join("yopa-store.dat")); |
|
|
|
|
|
|
|
store.load()?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(web::Data::new(tokio::sync::RwLock::new(store))) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|