Compare commits

..

No commits in common. 'master' and 'session-crate' have entirely different histories.

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

@ -15,8 +15,7 @@ use rocket_contrib::templates::Template;
//mod session; //mod session;
mod store; mod store;
mod session; use rocket_session::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,
@ -25,11 +24,12 @@ 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 crate::session::SessionAccess; use std::time::Duration;
#[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 as usize, count,
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()) .attach(Session::fairing(Duration::from_secs(3600)))
.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,59 +1,172 @@
use serde_json::Value; use json_dotpath::DotPaths;
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 json_dotpath::DotPaths; use serde_json::{Map, Value};
use std::collections::HashMap;
use std::time::{Instant, Duration};
use std::ops::Add;
const SESSION_ID: &'static str = "SESSID";
pub type Session<'a> = rocket_session::Session<'a, serde_json::Map<String, Value>>; type SessionsMap = HashMap<String, SessionInstance>;
pub trait SessionAccess { #[derive(Debug)]
fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T>; struct SessionInstance {
data: serde_json::Map<String, Value>,
expires: Instant,
}
#[derive(Default, Debug)]
pub struct SessionStore {
inner: RwLock<SessionsMap>,
lifespan: Duration,
}
fn get_or<T: DeserializeOwned>(&self, path: &str, def: T) -> T; #[derive(PartialEq, Hash, Clone, Debug)]
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(),
)
}
}))
}
}
fn get_or_else<T: DeserializeOwned, F: FnOnce() -> T>(&self, path: &str, def: F) -> T; #[derive(Debug)]
pub struct Session<'a> {
store: State<'a, SessionStore>,
id: &'a SessionID,
}
fn get_or_default<T: DeserializeOwned + Default>(&self, path: &str) -> T; impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> {
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(),
})
}
}
fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T>; impl<'a> Session<'a> {
pub fn fairing() -> impl Fairing {
SessionFairing
}
fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O>; fn tap<T>(&self, func : impl FnOnce(&mut serde_json::Map<String, Value>) -> T) -> T {
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
}
}
fn set<T: Serialize>(&self, path: &str, value: T); pub fn renew(&self) {
self.tap(|_| ())
}
fn remove(&self, path: &str) -> bool; pub fn reset(&self) {
self.tap(|data| data.clear())
} }
impl<'a> SessionAccess for Session<'a> { pub fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T> { self.tap(|data| data.dot_get(path))
self.tap(|data| data.dot_get(path).unwrap_or_default())
} }
fn get_or<T: DeserializeOwned>(&self, path: &str, def: T) -> T { pub fn get_or<T: DeserializeOwned>(&self, path: &str, def: T) -> T {
self.get(path).unwrap_or(def) self.get(path).unwrap_or(def)
} }
fn get_or_else<T: DeserializeOwned, F: FnOnce() -> T>(&self, path: &str, def: F) -> T { pub 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)
} }
fn get_or_default<T: DeserializeOwned + Default>(&self, path: &str) -> T { pub fn get_or_default<T: DeserializeOwned + Default>(&self, path: &str) -> T {
self.get(path).unwrap_or_default() self.get(path).unwrap_or_default()
} }
fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T> { pub fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
self.tap(|data| data.dot_take(path).unwrap_or_default()) self.tap(|data| data.dot_take(path))
} }
fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O> { pub fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O> {
self.tap(|data| data.dot_replace(path, new).unwrap_or_default()) self.tap(|data| data.dot_replace(path, new))
} }
fn set<T: Serialize>(&self, path: &str, value: T) { pub fn set<T: Serialize>(&self, path: &str, value: T) {
self.tap(|data| data.dot_set(path, value)).unwrap(); self.tap(|data| data.dot_set(path, value));
}
pub fn remove(&self, path: &str) -> bool {
self.tap(|data| data.dot_remove(path))
}
} }
fn remove(&self, path: &str) -> bool { /// Fairing struct
self.tap(|data| { data.dot_remove(path).is_ok() }) struct SessionFairing;
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).unwrap_or_default() { if let Some(value) = card.dot_get::<String>(&key) {
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).unwrap()); group.extend(card.dot_get_or_default::<Vec<String>>(&key));
} }
} }

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

Loading…
Cancel
Save