diff --git a/Cargo.lock b/Cargo.lock index 39f2ca4..89e086b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,6 +477,7 @@ dependencies = [ "actix-web", "bs62", "lazy_static", + "memcache", "ring", "serde", "serde_json", @@ -536,6 +537,18 @@ dependencies = [ "syn", ] +[[package]] +name = "enum_dispatch" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bead1f97ab8e54cd4f9205571b5c2c6f18abaa9193f9fdcf7fe09ddd4be313" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "flate2" version = "1.0.20" @@ -554,6 +567,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -901,6 +929,20 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "memcache" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c85d68ede7335fcbd6908253f90314ddfc4afb6bf79ec24833fe30f94cd48a1" +dependencies = [ + "byteorder", + "enum_dispatch", + "openssl", + "r2d2", + "rand", + "url", +] + [[package]] name = "memchr" version = "2.3.4" @@ -1028,6 +1070,33 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-sys" +version = "0.9.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52160d45fa2e7608d504b7c3a3355afed615e6d8b627a74458634ba21b69bd" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -1117,6 +1186,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1159,6 +1234,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r2d2" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + [[package]] name = "rand" version = "0.7.3" @@ -1266,6 +1352,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "scheduled-thread-pool" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +dependencies = [ + "parking_lot", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -1696,6 +1791,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "vcpkg" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d" + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 283f39e..d8ef28e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,5 @@ serde = "1" serde_json = "1" lazy_static = "1" ring = "0.16" -bs62 = "0.1" \ No newline at end of file +bs62 = "0.1" +memcache = "0.15" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..318927b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3.7' + +services: + memcached: + image: memcached:1-alpine + entrypoint: memcached -m 128 + ports: + - 11211:11211 diff --git a/src/note/model.rs b/src/note/model.rs index 0cfe838..ab48b59 100644 --- a/src/note/model.rs +++ b/src/note/model.rs @@ -7,14 +7,19 @@ pub struct Note { pub contents: String, pub password: bool, pub views: Option, - pub expiration: Option, + pub expiration: Option, } #[derive(Serialize, Deserialize, Clone)] -pub struct PubNote { +pub struct NoteInfo { pub password: bool, } +#[derive(Serialize, Deserialize, Clone)] +pub struct NotePublic { + pub contents: String, +} + pub fn generate_id() -> String { let mut id: [u8; 64] = [0; 64]; let sr = ring::rand::SystemRandom::new(); diff --git a/src/note/routes.rs b/src/note/routes.rs index cd7c580..34bb902 100644 --- a/src/note/routes.rs +++ b/src/note/routes.rs @@ -1,24 +1,40 @@ use actix_web::{delete, get, post, web, HttpResponse, Responder}; +use memcache; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::sync::Mutex; +use std::time::SystemTime; -use crate::note::{generate_id, Note, PubNote}; +use crate::note::{generate_id, Note, NoteInfo, NotePublic}; + +fn now() -> u64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() +} lazy_static! { - static ref RAM: Mutex> = { - let mut m = HashMap::new(); - m.insert( - "test".to_string(), - Note { - contents: "some stuff".to_string(), - password: false, - views: Some(1), - expiration: Some(100), - }, - ); - return Mutex::new(m); - }; + static ref CLIENT: memcache::Client = + memcache::connect("memcache://127.0.0.1:11211?timeout=10&tcp_nodelay=true").unwrap(); +} + +fn set(id: &String, note: &Note) { + let serialized = serde_json::to_string(¬e.clone()).unwrap(); + CLIENT.set(id, serialized, 0).unwrap(); +} + +fn get(id: &String) -> Option { + let value: Option = CLIENT.get(&id).unwrap(); + match value { + None => return None, + Some(s) => { + let deserialize: Note = serde_json::from_str(&s).unwrap(); + return Some(deserialize); + } + } +} + +fn del(id: &String) { + CLIENT.delete(id).unwrap(); } #[derive(Serialize, Deserialize)] @@ -28,25 +44,18 @@ struct NotePath { #[get("/notes/{id}")] async fn one(path: web::Path) -> impl Responder { - let ram = RAM.lock().unwrap(); let p = path.into_inner(); - let note = ram.get(&p.id); + let note = get(&p.id); match note { None => return HttpResponse::NotFound().finish(), Some(note) => { - return HttpResponse::Ok().json(PubNote { + return HttpResponse::Ok().json(NoteInfo { password: note.password, }) } } } -#[get("/notes/")] -async fn all() -> impl Responder { - let values: Vec = RAM.lock().unwrap().values().cloned().collect(); - return HttpResponse::Ok().json(values); -} - #[derive(Serialize, Deserialize)] struct CreateResponse { id: String, @@ -54,37 +63,68 @@ struct CreateResponse { #[post("/notes/")] async fn create(note: web::Json) -> impl Responder { - let n = note.into_inner(); + let mut n = note.into_inner(); let id = generate_id(); - RAM.lock().unwrap().insert(id.clone(), n.clone()); + let bad_req = HttpResponse::BadRequest().finish(); + if n.views == None && n.expiration == None { + return bad_req; + } + match n.views { + Some(v) => { + if v > 100 { + return bad_req; + } + } + _ => {} + } + match n.expiration { + Some(e) => { + if e > 360 { + return bad_req; + } + n.expiration = Some(now() + (e * 60)) + } + _ => {} + } + set(&id.clone(), &n.clone()); return HttpResponse::Ok().json(CreateResponse { id: id }); } #[delete("/notes/{id}")] async fn delete(path: web::Path) -> impl Responder { - let mut ram = RAM.lock().unwrap(); let p = path.into_inner(); - let note = ram.get(&p.id); + let note = get(&p.id); match note { None => return HttpResponse::NotFound().finish(), Some(note) => { let mut changed = note.clone(); if changed.views == None && changed.expiration == None { return HttpResponse::BadRequest().finish(); - } else { - match changed.views { - Some(v) => { - changed.views = Some(v - 1); - if v <= 1 { - ram.remove(&p.id); - } else { - ram.insert(p.id, changed.clone()); - } - } - _ => {} - } - return HttpResponse::Ok().json(changed); } + match changed.views { + Some(v) => { + changed.views = Some(v - 1); + let id = p.id.clone(); + if v <= 1 { + del(&id); + } else { + set(&id, &changed.clone()); + } + } + _ => {} + } + match changed.expiration { + Some(e) => { + if e > now() { + del(&p.id.clone()); + return HttpResponse::BadRequest().finish(); + } + } + _ => {} + } + return HttpResponse::Ok().json(NotePublic { + contents: changed.contents, + }); } } } @@ -93,5 +133,4 @@ pub fn init(cfg: &mut web::ServiceConfig) { cfg.service(create); cfg.service(delete); cfg.service(one); - cfg.service(all); }