changeset 41:f03f642c65a9

Add README
author Lewin Bormann <lbo@spheniscida.de>
date Thu, 03 Dec 2020 17:15:18 +0100
parents 99f70bfe5606
children 672230d9b0b5
files README.md src/main.rs
diffstat 2 files changed, 115 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Thu Dec 03 17:15:18 2020 +0100
@@ -0,0 +1,110 @@
+# `geohub`
+
+## What is GeoHub?
+
+GeoHub is -- in fancy terms -- a framework for real time geographic
+applications. It currently allows for
+
+* Ingesting geographic points, for example GPS positions, via HTTP API.
+* Exporting geographic points as GeoJSON (GPX soon to come).
+* Efficiently listening to position updates (new points) in real time via HTTP
+API.
+* Privacy first by protecting every point with a password (session token).
+
+## API description
+
+Important concepts are:
+
+* A **Client** is for example someone walking around with their phone, or a
+moving car. A client sends geographic updates to the server. The `client`
+appears as URL component after `/geo/`.
+* A **Secret** is an alphanumeric string (a-zA-Z0-9 only) attached to every
+point. Anyone wanting to retrieve a given point needs to know its secret. You
+can also log points without secret. In that case, everyone can see them
+(including clients asking for points with a secret). The `secret` is supplied as
+URL parameter `&secret=`.
+* Every point has a unique integer **ID**. It can be used to limit which points
+to fetch, for example when refreshing or waiting on updates. Think of it as a
+very fine-grained page token.
+
+*Scenario*: You go for a walk, configuring your phone to send live updates to a
+GeoHub instance. You want to share it with a friend who is not supposed to know
+about where you were yesterday. You can leave your `client` string the same and
+use a new `secret` that you give to your friend; they can now only see points
+logged with this secret.
+
+* `/geo/<client>/log?lat=<latitude>&longitude=<longitude>&time=<time>&s=<speed>&ele=<elevation>&secret=<secret>`
+  * Log a new point.
+  * `latitude`, `longitude`: Geographical position, in decimal degrees (note:
+      may be extended later). **Required**.
+  * `time`: ISO 8601 time. If left out, current server time is used. Example:
+  `2020-12-03T15:42:40.010325Z`. **Optional**.
+  * `speed`: Speed in km/h (usually). If you decide to always use m/s, you are
+  free to do so. **Optional**.
+  * `elevation`: Elevation in meters. **Optional**.
+  * Usually returns code **200** except for server errors (500) or malformed inputs (400).
+* `/geo/assets/...`
+  * Static file serving. The `assets` directory should be deployed in the
+  current working directory from which the server is run.
+* `/geo/<client>/retrieve/json?secret=<secret>&from=<from_timestamp>&to=<to_timestamp>&limit=<maximum
+number of entries returned>&last=<id of last known entry>`
+  * Fetch geo data as GeoJSON object.
+  * `from`, `to`: Timestamp range. For best results, supply ISO 8601 timestamps,
+  but `YYYY-mm-dd hh:mm:ss.sss` is also accepted. (GeoHub tries to be flexible
+  about this, and may become better over time).
+  * `limit`: Return at most this number of entries, starting with the oldest
+  entries.
+  * `last`: This is a sort of page token, identifying the most recent entry you
+  know. GeoHub will only return events newer than this. The IDs used here are
+  returned as property `id` in the GeoJSON `Feature`s.
+  * Returns a GeoJSON object.
+* `/geo/<client>/retrieve/last?secret=<secret>&last=<last ID>&limit=<max
+entries>`
+  * Fetch most recent points for the `client`. See `/geo/<client>/retrieve/json`
+  above for descriptions of the other parameters.
+  * Returns a `LiveUpdate` object. `last` is the most-recent ID of all points:
+
+```json
+{
+  "type": "GeoHubUpdate",
+  "last": 1205,
+  "error": "error string if applicable",
+  "geo": {
+    "type": "FeatureCollection",
+    "features": [
+      {
+        "type": "Feature",
+        "properties": {
+          "time": "2020-12-03T15:42:40.010325Z",
+          "altitude": 40,
+          "speed": 22,
+          "id": 1205
+        },
+        "geometry": {
+          "type": "Point",
+          "coordinates": [
+            6.09,
+            50.795
+          ]
+        }
+      },
+    ...
+    ]
+  }
+}
+```
+
+* `/geo/<client>/retrieve/live?secret=<secret>&timeout=<timeout in sec>`
+  * Wait at most `timeout` seconds for events from `client` with the given
+  `secret`. This is a "hanging" request endpoint, returning after `timeout`
+  seconds or any time that a new point has been logged. This is useful for
+  real-time applications, such as the live map (in `assets/`).
+  * This API returns data compatible with `/geo/<client>/retrieve/last`. Think
+  of the two endpoints as complementary, one retrieving recent and this one
+  returning current events.
+  * Note: As opposed to other endpoints, this endpoint doesn't return points
+  with lacking secret ("public" points).
+  * This endpoint returns at most one point at a time.
+  * If no new point has arrived in time, a `LiveUpdate` with `null` entries for
+  `geo` and `last` is returned.
+
--- a/src/main.rs	Thu Dec 03 16:42:55 2020 +0100
+++ b/src/main.rs	Thu Dec 03 17:15:18 2020 +0100
@@ -55,19 +55,19 @@
 }
 
 /// Retrieve GeoJSON data.
-#[rocket::get("/geo/<name>/retrieve/json?<secret>&<from>&<to>&<limit>&<last>")]
+#[rocket::get("/geo/<client>/retrieve/json?<secret>&<from>&<to>&<limit>&<last>")]
 fn retrieve_json(
     db: db::DBConn,
-    name: String,
+    client: String,
     secret: Option<String>,
     from: Option<String>,
     to: Option<String>,
     limit: Option<i64>,
     last: Option<i32>,
 ) -> http::GeoHubResponse {
-    if !ids::name_and_secret_acceptable(name.as_str(), secret.as_ref().map(|s| s.as_str())) {
+    if !ids::name_and_secret_acceptable(client.as_str(), secret.as_ref().map(|s| s.as_str())) {
         return http::bad_request(
-            "You have supplied an invalid secret or name. Both must be ASCII alphanumeric strings."
+            "You have supplied an invalid secret or client. Both must be ASCII alphanumeric strings."
                 .into(),
         );
     }
@@ -83,7 +83,7 @@
         .unwrap_or(chrono::Utc::now());
     let limit = limit.unwrap_or(16384);
 
-    let result = db.retrieve_json(name.as_str(), from_ts, to_ts, &secret, limit, last);
+    let result = db.retrieve_json(client.as_str(), from_ts, to_ts, &secret, limit, last);
     match result {
         Ok(json) => http::return_json(&json),
         Err(e) => http::server_error(e.to_string()),