Mercurial > lbo > hg > geohub
changeset 8:a5de18a5e99e
Implement GeoJSON retrieval and per-point protection
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Tue, 01 Dec 2020 21:22:08 +0100 |
parents | ebcf9edce874 |
children | b8b99af28199 |
files | Cargo.lock Cargo.toml pgsql_schema.sql src/main.rs |
diffstat | 4 files changed, 208 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/Cargo.lock Tue Dec 01 20:28:22 2020 +0100 +++ b/Cargo.lock Tue Dec 01 21:22:08 2020 +0100 @@ -199,6 +199,7 @@ "libc", "num-integer", "num-traits", + "serde", "time", "winapi 0.3.9", ] @@ -271,7 +272,7 @@ checksum = "066ceb7928ca93a9bedc6d0e612a8a0424048b0ab1f75971b203d01420c055d7" dependencies = [ "devise_core", - "quote", + "quote 0.6.13", ] [[package]] @@ -281,9 +282,9 @@ checksum = "cf41c59b22b5e3ec0ea55c7847e5f358d340f3a8d6d53a5cf4f1564967f96487" dependencies = [ "bitflags", - "proc-macro2", - "quote", - "syn", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", ] [[package]] @@ -395,6 +396,8 @@ "postgres", "rocket", "rocket_contrib", + "serde", + "serde_json", ] [[package]] @@ -559,6 +562,12 @@ ] [[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -797,9 +806,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfc1c836fdc3d1ef87c348b237b5b5c4dff922156fb2d968f57734f9669768ca" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", "version_check 0.9.2", "yansi", ] @@ -902,7 +911,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid", + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid 0.2.1", ] [[package]] @@ -911,7 +929,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "proc-macro2", + "proc-macro2 0.4.30", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2 1.0.24", ] [[package]] @@ -1060,7 +1087,7 @@ "devise", "glob", "indexmap", - "quote", + "quote 0.6.13", "rocket_http", "version_check 0.9.2", "yansi", @@ -1088,7 +1115,7 @@ checksum = "fd30b50723c98c7ba6eea01cc5416e273397fa09ac45569538758ae24590dd7d" dependencies = [ "devise", - "quote", + "quote 0.6.13", "version_check 0.9.2", "yansi", ] @@ -1107,10 +1134,16 @@ "smallvec", "state", "time", - "unicode-xid", + "unicode-xid 0.1.0", ] [[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] name = "safemem" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1151,6 +1184,31 @@ version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.7", + "syn 1.0.53", +] + +[[package]] +name = "serde_json" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +dependencies = [ + "itoa", + "ryu", + "serde", +] [[package]] name = "sha2" @@ -1240,9 +1298,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + +[[package]] +name = "syn" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.7", + "unicode-xid 0.2.1", ] [[package]] @@ -1332,6 +1401,12 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] name = "universal-hash" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index"
--- a/Cargo.toml Tue Dec 01 20:28:22 2020 +0100 +++ b/Cargo.toml Tue Dec 01 21:22:08 2020 +0100 @@ -9,7 +9,9 @@ [dependencies] rocket = "~0.4" postgres = { version = "~0.15", features = ["with-chrono"] } -chrono = "^0.4" +chrono = { version = "^0.4", features = ["serde"] } +serde = { version = "~1.0", features = ["derive"] } +serde_json = "~1.0" [dependencies.rocket_contrib] version = "~0.4"
--- a/pgsql_schema.sql Tue Dec 01 20:28:22 2020 +0100 +++ b/pgsql_schema.sql Tue Dec 01 21:22:08 2020 +0100 @@ -32,7 +32,7 @@ lat double precision, long double precision, spd double precision, - t timestamp with time zone, + t timestamp with time zone not null, ele double precision );
--- a/src/main.rs Tue Dec 01 20:28:22 2020 +0100 +++ b/src/main.rs Tue Dec 01 21:22:08 2020 +0100 @@ -31,9 +31,118 @@ None } +/// Fetch geodata as JSON. +/// +#[derive(serde::Serialize, Debug)] +struct GeoProperties { + time: chrono::DateTime<chrono::Utc>, + altitude: Option<f64>, + speed: Option<f64>, +} + +#[derive(serde::Serialize, Debug)] +struct GeoGeometry { + #[serde(rename = "type")] + typ: String, // always "Point" + coordinates: Vec<f64>, // always [long, lat] +} + +#[derive(serde::Serialize, Debug)] +struct GeoFeature { + #[serde(rename = "type")] + typ: String, // always "Feature" + properties: GeoProperties, + geometry: GeoGeometry, +} + +fn geofeature_from_row( + ts: chrono::DateTime<chrono::Utc>, + lat: Option<f64>, + long: Option<f64>, + spd: Option<f64>, + ele: Option<f64>, +) -> GeoFeature { + GeoFeature { + typ: "Feature".into(), + properties: GeoProperties { + time: ts, + altitude: ele, + speed: spd, + }, + geometry: GeoGeometry { + typ: "Point".into(), + coordinates: vec![long.unwrap_or(0.), lat.unwrap_or(0.)], + }, + } +} + +#[derive(serde::Serialize, Debug)] +struct GeoJSON { + #[serde(rename = "type")] + typ: String, // always "FeatureCollection" + features: Vec<GeoFeature>, +} + +/// Retrieve GeoJSON data. +#[rocket::get("/geo/<name>/retrieve/json?<secret>&<from>&<to>&<max>")] +fn retrieve_json( + db: DBConn, + name: String, + secret: Option<String>, + from: Option<String>, + to: Option<String>, + max: Option<i64>, +) -> rocket::response::content::Json<String> { + let mut returnable = GeoJSON { + typ: "FeatureCollection".into(), + features: vec![], + }; + + let from_ts = from + .and_then(flexible_timestamp_parse) + .unwrap_or(chrono::DateTime::from_utc( + chrono::NaiveDateTime::from_timestamp(0, 0), + chrono::Utc, + )); + let to_ts = to + .and_then(flexible_timestamp_parse) + .unwrap_or(chrono::Utc::now()); + let max = max.unwrap_or(16384); + //println!("from {:?} to {:?}", from_ts, to_ts); + //println!("secret {:?}", secret); + + let stmt = db.0.prepare_cached( + r"SELECT t, lat, long, spd, ele FROM geohub.geodata + WHERE (id = $1) and (t between $2 and $3) AND (secret = public.digest($4, 'sha256') or secret is null) + LIMIT $5").unwrap(); // Must succeed. + let rows = stmt + .query(&[&name, &from_ts, &to_ts, &secret, &max]) + .unwrap(); + { + println!("got {} rows", rows.len()); + returnable.features = Vec::with_capacity(rows.len()); + for row in rows.iter() { + let (ts, lat, long, spd, ele): ( + chrono::DateTime<chrono::Utc>, + Option<f64>, + Option<f64>, + Option<f64>, + Option<f64>, + ) = (row.get(0), row.get(1), row.get(2), row.get(3), row.get(4)); + returnable + .features + .push(geofeature_from_row(ts, lat, long, spd, ele)); + } + } + + rocket::response::content::Json(serde_json::to_string(&returnable).unwrap()) +} + +/// Ingest geo data. + /// time is like 2020-11-30T20:12:36.444Z (ISO 8601). By default, server time is set. /// secret can be used to protect points. -#[rocket::get("/geo/<name>/log?<lat>&<longitude>&<time>&<s>&<ele>&<secret>")] +#[rocket::post("/geo/<name>/log?<lat>&<longitude>&<time>&<s>&<ele>&<secret>")] fn log( db: DBConn, name: String, @@ -51,17 +160,15 @@ if let Some(time) = time { ts = flexible_timestamp_parse(time).unwrap_or(ts); } - db.0.execute( - "INSERT INTO geohub.geodata (id, lat, long, spd, t, ele, secret) VALUES ($1, $2, $3, $4, $5, $6, $7)", - &[&name, &lat, &longitude, &s, &ts, &ele, &secret], - ) - .unwrap(); + let stmt = db.0.prepare_cached("INSERT INTO geohub.geodata (id, lat, long, spd, t, ele, secret) VALUES ($1, $2, $3, $4, $5, $6, public.digest($7, 'sha256'))").unwrap(); + stmt.execute(&[&name, &lat, &longitude, &s, &ts, &ele, &secret]) + .unwrap(); rocket::http::Status::Ok } fn main() { rocket::ignite() .attach(DBConn::fairing()) - .mount("/", rocket::routes![log]) + .mount("/", rocket::routes![log, retrieve_json]) .launch(); }