Compare commits

...

2 Commits

  1. 1414
      Cargo.lock
  2. 29
      Cargo.toml
  3. 12
      src/main.rs
  4. 165
      src/session.rs
  5. 4
      src/store/mod.rs
  6. 1
      templates/index.html.tera

1414
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -9,22 +9,23 @@ edition = "2018"
[dependencies] [dependencies]
#rocket-download-response = "0.4.9" #rocket-download-response = "0.4.9"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = { version="1.0", features= ["preserve_order"] } serde_json = { version="1", features= ["preserve_order"] }
serde_yaml = "0.8.11" serde_yaml = "0.8.16"
json_dotpath = "0.1.2" json_dotpath = "1.0.3"
titlecase = "0.10.0" titlecase = "1.1.0"
rand = "0.7.2" rand = "0.8.3"
rocket_session = { git = "https://git.ondrovo.com/packages/rocket_session.git" } rocket_session = "0.2"
#rocket_session = { path = "../rocket-session" }
# special data structure that preserves order # special data structure that preserves order
indexmap = { version="1.3.0", features=["serde-1"] } indexmap = { version="1.6.1", features=["serde-1"] }
rocket = { version="0.4.2", default-features = false} rocket = { version="0.4.6", default-features = false}
rocket_contrib = { version="0.4.2", features = ["json", "serve", "tera_templates"]} rocket_contrib = { version="0.4.6", features = ["json", "serve", "tera_templates"]}
ifmt = "0.2.0" ifmt = "0.3"
parking_lot = "0.10.0" parking_lot = "0.11"
lazy_static = "1.4.0" lazy_static = "1.4"
linked-hash-map = "0.5.2" linked-hash-map = "0.5"

@ -15,7 +15,8 @@ use rocket_contrib::templates::Template;
//mod session; //mod session;
mod store; mod store;
use rocket_session::Session; mod session;
use session::Session;
use crate::store::form::{ use crate::store::form::{
collect_card_form, render_card_fields, render_empty_fields, MapFromForm, RenderedCard, collect_card_form, render_card_fields, render_empty_fields, MapFromForm, RenderedCard,
@ -24,12 +25,11 @@ use crate::store::form::{
use crate::store::Store; use crate::store::Store;
use parking_lot::RwLock; use parking_lot::RwLock;
//use crate::session::Session;
use rocket::request::Form; use rocket::request::Form;
use rocket::response::Redirect; use rocket::response::Redirect;
use rocket::State; use rocket::State;
use std::env; use std::env;
use std::time::Duration; use crate::session::SessionAccess;
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct ListContext<'a> { pub struct ListContext<'a> {
@ -64,7 +64,7 @@ fn route_index(store: State<RwLock<Store>>, session: Session, page: Option<usize
count += 1; count += 1;
session.set("foo.bar.count", count); session.set("foo.bar.count", count);
println!("{:?}", session); //println!("{:?}", session);
let mut page = page.unwrap_or_default(); let mut page = page.unwrap_or_default();
let n_pages = (rg.data.cards.len() as f64 / PER_PAGE as f64).ceil() as usize; let n_pages = (rg.data.cards.len() as f64 / PER_PAGE as f64).ceil() as usize;
@ -77,7 +77,7 @@ fn route_index(store: State<RwLock<Store>>, session: Session, page: Option<usize
fields: render_empty_fields(&rg), fields: render_empty_fields(&rg),
pages: n_pages, pages: n_pages,
page, page,
count, count: count as usize,
cards: rg cards: rg
.data .data
.cards .cards
@ -190,7 +190,7 @@ fn main() {
rocket::ignite() rocket::ignite()
.attach(Template::fairing()) .attach(Template::fairing())
.attach(Session::fairing(Duration::from_secs(3600))) .attach(Session::fairing())
.manage(RwLock::new(store)) .manage(RwLock::new(store))
.mount("/", StaticFiles::from(cwd.join("templates/static/"))) .mount("/", StaticFiles::from(cwd.join("templates/static/")))
.mount( .mount(

@ -1,172 +1,59 @@
use json_dotpath::DotPaths; use serde_json::Value;
use parking_lot::RwLock;
use rand::Rng;
use rocket::fairing::{self, Fairing, Info};
use rocket::request::FromRequest;
use rocket::{
http::{Cookie, Status},
Outcome, Request, Response, Rocket, State,
};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use serde_json::{Map, Value}; use json_dotpath::DotPaths;
use std::collections::HashMap;
use std::time::{Instant, Duration};
use std::ops::Add;
const SESSION_ID: &'static str = "SESSID";
type SessionsMap = HashMap<String, SessionInstance>;
#[derive(Debug)] pub type Session<'a> = rocket_session::Session<'a, serde_json::Map<String, Value>>;
struct SessionInstance {
data: serde_json::Map<String, Value>,
expires: Instant,
}
#[derive(Default, Debug)] pub trait SessionAccess {
pub struct SessionStore { fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T>;
inner: RwLock<SessionsMap>,
lifespan: Duration,
}
#[derive(PartialEq, Hash, Clone, Debug)] fn get_or<T: DeserializeOwned>(&self, path: &str, def: T) -> T;
pub struct SessionID(String);
impl<'a, 'r> FromRequest<'a, 'r> for &'a SessionID {
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<Self, (Status, Self::Error), ()> {
Outcome::Success(request.local_cache(|| {
println!("get id");
if let Some(cookie) = request.cookies().get(SESSION_ID) {
println!("from cookie");
SessionID(cookie.value().to_string()) // FIXME avoid cloning
} else {
println!("new id");
SessionID(
rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect(),
)
}
}))
}
}
#[derive(Debug)] fn get_or_else<T: DeserializeOwned, F: FnOnce() -> T>(&self, path: &str, def: F) -> T;
pub struct Session<'a> {
store: State<'a, SessionStore>,
id: &'a SessionID,
}
impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> { fn get_or_default<T: DeserializeOwned + Default>(&self, path: &str) -> T;
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<Self, (Status, Self::Error), ()> {
Outcome::Success(Session {
id: request.local_cache(|| {
if let Some(cookie) = request.cookies().get(SESSION_ID) {
SessionID(cookie.value().to_string())
} else {
SessionID(
rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect(),
)
}
}),
store: request.guard().unwrap(),
})
}
}
impl<'a> Session<'a> { fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T>;
pub fn fairing() -> impl Fairing {
SessionFairing
}
fn tap<T>(&self, func : impl FnOnce(&mut serde_json::Map<String, Value>) -> T) -> T { fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O>;
let mut wg = self.store.inner.write();
if let Some(instance) = wg.get_mut(&self.id.0) {
instance.expires = Instant::now().add(self.store.lifespan);
func(&mut instance.data)
} else {
let mut data = Map::new();
let rv = func(&mut data);
wg.insert(self.id.0.clone(), SessionInstance {
data: data,
expires: Instant::now().add(self.store.lifespan),
});
rv
}
}
pub fn renew(&self) { fn set<T: Serialize>(&self, path: &str, value: T);
self.tap(|_| ())
}
pub fn reset(&self) { fn remove(&self, path: &str) -> bool;
self.tap(|data| data.clear())
} }
pub fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T> { impl<'a> SessionAccess for Session<'a> {
self.tap(|data| data.dot_get(path)) fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
self.tap(|data| data.dot_get(path).unwrap_or_default())
} }
pub fn get_or<T: DeserializeOwned>(&self, path: &str, def: T) -> T { fn get_or<T: DeserializeOwned>(&self, path: &str, def: T) -> T {
self.get(path).unwrap_or(def) self.get(path).unwrap_or(def)
} }
pub fn get_or_else<T: DeserializeOwned, F: FnOnce() -> T>(&self, path: &str, def: F) -> T { fn get_or_else<T: DeserializeOwned, F: FnOnce() -> T>(&self, path: &str, def: F) -> T {
self.get(path).unwrap_or_else(def) self.get(path).unwrap_or_else(def)
} }
pub fn get_or_default<T: DeserializeOwned + Default>(&self, path: &str) -> T { fn get_or_default<T: DeserializeOwned + Default>(&self, path: &str) -> T {
self.get(path).unwrap_or_default() self.get(path).unwrap_or_default()
} }
pub fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T> { fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
self.tap(|data| data.dot_take(path)) self.tap(|data| data.dot_take(path).unwrap_or_default())
} }
pub fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O> { fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O> {
self.tap(|data| data.dot_replace(path, new)) self.tap(|data| data.dot_replace(path, new).unwrap_or_default())
} }
pub fn set<T: Serialize>(&self, path: &str, value: T) { fn set<T: Serialize>(&self, path: &str, value: T) {
self.tap(|data| data.dot_set(path, value)); self.tap(|data| data.dot_set(path, value)).unwrap();
}
pub fn remove(&self, path: &str) -> bool {
self.tap(|data| data.dot_remove(path))
}
} }
/// Fairing struct fn remove(&self, path: &str) -> bool {
struct SessionFairing; self.tap(|data| { data.dot_remove(path).is_ok() })
impl Fairing for SessionFairing {
fn info(&self) -> Info {
Info {
name: "Session Fairing",
kind: fairing::Kind::Attach | fairing::Kind::Response,
}
} }
fn on_attach(&self, rocket: Rocket) -> Result<Rocket, Rocket> {
Ok(rocket.manage(SessionStore::default()))
} }
fn on_response<'r>(&self, request: &'r Request, response: &mut Response) {
let session = request.local_cache(|| SessionID("".to_string()));
if !session.0.is_empty() {
response.adjoin_header(Cookie::build(SESSION_ID, session.0.clone()).finish());
}
}
}

@ -165,7 +165,7 @@ impl Store {
let group = index.free_enums.get_mut(group.as_str()).unwrap(); let group = index.free_enums.get_mut(group.as_str()).unwrap();
if let Some(value) = card.dot_get::<String>(&key) { if let Some(value) = card.dot_get::<String>(&key).unwrap_or_default() {
if !value.is_empty() { if !value.is_empty() {
group.insert(value.to_string()); group.insert(value.to_string());
} }
@ -179,7 +179,7 @@ impl Store {
let group = index.free_tags.get_mut(group.as_str()).unwrap(); let group = index.free_tags.get_mut(group.as_str()).unwrap();
group.extend(card.dot_get_or_default::<Vec<String>>(&key)); group.extend(card.dot_get_or_default::<Vec<String>>(&key).unwrap());
} }
} }

@ -11,7 +11,6 @@
{%- endblock %} {%- endblock %}
{% block content -%} {% block content -%}
COUNT {{count}}
<table class="cards-table"> <table class="cards-table">
<thead> <thead>

Loading…
Cancel
Save