|
|
|
@ -18,9 +18,13 @@ use std::borrow::Borrow; |
|
|
|
|
use std::ops::Deref; |
|
|
|
|
use yopa::{Storage, TypedValue}; |
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
use actix_session::CookieSession; |
|
|
|
|
use rand::Rng; |
|
|
|
|
use actix_web_static_files::ResourceFiles as StaticFiles; |
|
|
|
|
|
|
|
|
|
mod tera_ext; |
|
|
|
|
mod routes; |
|
|
|
|
mod session_ext; |
|
|
|
|
|
|
|
|
|
// Embed static files
|
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/static_files.rs")); |
|
|
|
@ -30,7 +34,7 @@ static TEMPLATES: include_dir::Dir = include_dir::include_dir!("./resources/temp |
|
|
|
|
|
|
|
|
|
pub(crate) static TERA : Lazy<Tera> = Lazy::new(|| { |
|
|
|
|
let mut tera = Tera::default(); |
|
|
|
|
tera.add_include_dir_templates(&TEMPLATES); |
|
|
|
|
tera.add_include_dir_templates(&TEMPLATES).unwrap(); |
|
|
|
|
|
|
|
|
|
// Special filter for the TypedValue map
|
|
|
|
|
use serde_json::Value; |
|
|
|
@ -46,6 +50,16 @@ pub(crate) static TERA : Lazy<Tera> = Lazy::new(|| { |
|
|
|
|
Err(tera::Error::msg("Expected nonenmpty object")) |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// TODO need to inject HttpRequest::url_for() into tera context, but it then can't be accessed by the functions.
|
|
|
|
|
// tera.register_function("url_for", |args: HashMap<String, Value>| -> tera::Result<Value> {
|
|
|
|
|
// match args.get("name") {
|
|
|
|
|
// Some(Value::String(s)) => {
|
|
|
|
|
// let r =
|
|
|
|
|
// },
|
|
|
|
|
// _ => Err("Expected string argument".into()),
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
tera |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
@ -58,98 +72,117 @@ async fn main() -> std::io::Result<()> { |
|
|
|
|
// Ensure the lazy ref is initialized early (to catch template bugs ASAP)
|
|
|
|
|
let _ = TERA.deref(); |
|
|
|
|
|
|
|
|
|
let database : YopaStoreWrapper = { |
|
|
|
|
let mut store = Storage::new(); |
|
|
|
|
|
|
|
|
|
// Seed the store with some dummy data for view development
|
|
|
|
|
use yopa::model; |
|
|
|
|
use yopa::DataType; |
|
|
|
|
|
|
|
|
|
let id_recipe = store.define_object(model::ObjectModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
name: "Recipe".to_string(), |
|
|
|
|
parent: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
let id_book = store.define_object(model::ObjectModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
name: "Book".to_string(), |
|
|
|
|
parent: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
let id_ing = store.define_object(model::ObjectModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
name: "Ingredient".to_string(), |
|
|
|
|
parent: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_recipe, |
|
|
|
|
name: "name".to_string(), |
|
|
|
|
optional: false, |
|
|
|
|
multiple: true, |
|
|
|
|
data_type: DataType::String, |
|
|
|
|
default: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_book, |
|
|
|
|
name: "title".to_string(), |
|
|
|
|
optional: false, |
|
|
|
|
multiple: false, |
|
|
|
|
data_type: DataType::String, |
|
|
|
|
default: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_book, |
|
|
|
|
name: "author".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: true, |
|
|
|
|
data_type: DataType::String, |
|
|
|
|
default: Some(TypedValue::String("Pepa Novák".into())) |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
let rel_book_id = store.define_relation(model::RelationModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_recipe, |
|
|
|
|
name: "book reference".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: true, |
|
|
|
|
related: id_book |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: rel_book_id, |
|
|
|
|
name: "page".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: false, |
|
|
|
|
data_type: DataType::Integer, |
|
|
|
|
default: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_relation(model::RelationModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_recipe, |
|
|
|
|
name: "related recipe".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: true, |
|
|
|
|
related: id_recipe |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
web::Data::new(tokio::sync::RwLock::new(store)) |
|
|
|
|
}; |
|
|
|
|
let yopa_store: YopaStoreWrapper = init_yopa(); |
|
|
|
|
|
|
|
|
|
let mut session_key = [0u8; 32]; |
|
|
|
|
rand::thread_rng().fill(&mut session_key); |
|
|
|
|
|
|
|
|
|
debug!("Session key: {:?}", session_key); |
|
|
|
|
|
|
|
|
|
HttpServer::new(move || { |
|
|
|
|
let static_files = actix_web_static_files::ResourceFiles::new("/static", included_static_files()) |
|
|
|
|
let static_files = StaticFiles::new("/static", included_static_files()) |
|
|
|
|
.do_not_resolve_defaults(); |
|
|
|
|
|
|
|
|
|
App::new() |
|
|
|
|
.app_data(database.clone()) |
|
|
|
|
/* Middlewares */ |
|
|
|
|
.wrap( |
|
|
|
|
CookieSession::signed(&session_key) |
|
|
|
|
.secure(false) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
/* Bind shared objects */ |
|
|
|
|
.app_data(yopa_store.clone()) |
|
|
|
|
|
|
|
|
|
/* Routes */ |
|
|
|
|
.service(routes::index) |
|
|
|
|
.service(routes::object_model_create_form) |
|
|
|
|
.service(routes::object_model_create) |
|
|
|
|
.service(static_files) |
|
|
|
|
.default_service(web::to(|| HttpResponse::NotFound().body("Not found"))) |
|
|
|
|
}) |
|
|
|
|
.bind("127.0.0.1:8080")?.run().await |
|
|
|
|
.bind("127.0.0.1:8080")? |
|
|
|
|
.run().await |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn init_yopa() -> YopaStoreWrapper { |
|
|
|
|
let mut store = Storage::new(); |
|
|
|
|
|
|
|
|
|
// Seed the store with some dummy data for view development
|
|
|
|
|
use yopa::model; |
|
|
|
|
use yopa::DataType; |
|
|
|
|
|
|
|
|
|
let id_recipe = store.define_object(model::ObjectModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
name: "Recipe".to_string(), |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
let id_book = store.define_object(model::ObjectModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
name: "Book".to_string(), |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
let id_ing = store.define_object(model::ObjectModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
name: "Ingredient".to_string(), |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_recipe, |
|
|
|
|
name: "name".to_string(), |
|
|
|
|
optional: false, |
|
|
|
|
multiple: true, |
|
|
|
|
data_type: DataType::String, |
|
|
|
|
default: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_book, |
|
|
|
|
name: "title".to_string(), |
|
|
|
|
optional: false, |
|
|
|
|
multiple: false, |
|
|
|
|
data_type: DataType::String, |
|
|
|
|
default: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_book, |
|
|
|
|
name: "author".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: true, |
|
|
|
|
data_type: DataType::String, |
|
|
|
|
default: Some(TypedValue::String("Pepa Novák".into())) |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
let rel_book_id = store.define_relation(model::RelationModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_recipe, |
|
|
|
|
name: "book reference".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: true, |
|
|
|
|
related: id_book |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_property(model::PropertyModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: rel_book_id, |
|
|
|
|
name: "page".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: false, |
|
|
|
|
data_type: DataType::Integer, |
|
|
|
|
default: None |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
store.define_relation(model::RelationModel { |
|
|
|
|
id: Default::default(), |
|
|
|
|
object: id_recipe, |
|
|
|
|
name: "related recipe".to_string(), |
|
|
|
|
optional: true, |
|
|
|
|
multiple: true, |
|
|
|
|
related: id_recipe |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
web::Data::new(tokio::sync::RwLock::new(store)) |
|
|
|
|
} |
|
|
|
|