//! JSON types shared by the server, the CLI and the web UI. use serde::{Deserialize, Serialize}; /// Unix seconds. pub type Ts = i64; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Point { pub ts: Ts, pub lat: f64, pub lon: f64, /// Metres. #[serde(default)] pub acc: Option, /// Metres. #[serde(default)] pub alt: Option, /// m/s. #[serde(default)] pub speed: Option, /// Degrees. #[serde(default)] pub bearing: Option, /// Percent. #[serde(default)] pub battery: Option, } /// Someone whose position the caller may see. Always includes the caller. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Person { pub id: i64, pub username: String, /// The visible devices that have a position, newest first. pub devices: Vec, pub trail: Trail, /// Positions are rounded to about this many metres. 0 means exact. pub precision_m: u32, } impl Person { /// The device with the newest position. Its position is the person's position. pub fn last(&self) -> Option<&PersonDevice> { self.devices.first() } } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct PersonDevice { pub id: i64, pub name: String, pub last: Point, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Me { pub id: i64, pub username: String, pub is_admin: bool, pub has_password: bool, /// Sign-in needs the password and a passkey. Otherwise either one is enough. pub two_factor: bool, /// The user's own limit. None means the server limit. pub retention_days: Option, /// The server limit. None means points are kept forever. pub max_retention_days: Option, /// The address browsers use, if the server knows it. pub public_url: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SetupStatus { /// True while no user exists. The first account becomes the admin. pub needed: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Credentials { pub username: String, pub password: String, } #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct Login { #[serde(default)] pub username: String, pub password: String, /// Set when a passkey sign-in asked for the password as the second step. #[serde(default)] pub state_id: Option, } /// Reply to a sign-in step. `ok` means the session cookie is set. #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct LoginResult { pub ok: bool, /// The password was right. The account also needs a passkey. #[serde(default)] pub passkey_challenge: Option, /// The passkey was right. The account also needs its password, sent with this state_id. #[serde(default)] pub password_required: Option, } /// The first leg of a WebAuthn ceremony. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Challenge { pub state_id: String, /// The options for `navigator.credentials`, as JSON. pub options: String, } /// The second leg of a WebAuthn ceremony. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ChallengeAnswer { pub state_id: String, /// The browser's `PublicKeyCredential.toJSON()`, as JSON. pub credential: String, /// A label for a new passkey. Ignored when signing in. #[serde(default)] pub name: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Passkey { pub id: i64, pub name: String, pub created_at: Ts, pub last_used_at: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ChangePassword { /// Required when the account has a password. pub old: Option, pub new: String, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SetTwoFactor { pub enabled: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SetRetention { /// None means the server limit. pub days: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NewDevice { pub name: String, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct DeviceToken { pub token: String, } /// Starts pairing an app. The web UI sends it for the signed-in user. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PairBegin { /// SHA-256 in hex of a secret that only the app knows. pub challenge: String, pub name: String, } /// A one-time code for the app, valid 5 minutes. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PairCode { pub code: String, } /// The app exchanges the code and its secret for a device token. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PairFinish { pub code: String, pub verifier: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Device { pub id: i64, pub name: String, /// The one device that every browser with the web UI uploads as. It has no token. pub web: bool, pub created_at: Ts, pub last_seen_at: Option, } /// Reply to a point upload. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Uploaded { /// Points that were new. Duplicates of stored points are ignored. pub stored: usize, /// Invalid points, for example with a timestamp far in the future. They are dropped, so they cannot block a client's queue. pub skipped: usize, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NewShare { pub viewer: String, /// None means no expiry. pub expires_at: Option, #[serde(flatten)] pub settings: ShareSettings, } /// How much of the history a viewer sees besides the current position. #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Default)] #[serde(rename_all = "snake_case")] pub enum Trail { None, /// Points from this time on. Since(Ts), #[default] All, } /// What a share or guest link shows. Sharing again with the same viewer replaces these. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] pub struct ShareSettings { /// The owner's device ids. None means all devices, including ones added later. #[serde(default)] pub devices: Option>, #[serde(default)] pub trail: Trail, /// Rounds positions to about this many metres. 0 means exact. #[serde(default)] pub precision_m: u32, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NewLink { /// A label for the owner, for example whom the link is for. pub name: String, pub expires_at: Option, #[serde(flatten)] pub settings: ShareSettings, /// Guests must enter it before they see anything. #[serde(default)] pub password: Option, } /// A guest link: anyone with the token can see what it shows. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Link { pub id: i64, pub name: String, pub token: String, pub expires_at: Option, pub created_at: Ts, pub has_password: bool, #[serde(flatten)] pub settings: ShareSettings, } /// Identifies a guest request. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct GuestAuth { pub token: String, /// From GuestKey. Needed when the link has a password. #[serde(default)] pub key: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct GuestUnlock { pub token: String, pub password: String, } /// Proves the link password. It stops working when the link is deleted. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct GuestKey { pub key: String, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct GuestView { pub expires_at: Option, pub person: Person, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct GuestTrack { #[serde(flatten)] pub auth: GuestAuth, pub device: i64, pub from: Ts, pub to: Ts, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Share { pub id: i64, /// The other party: the viewer for outgoing shares, the owner for incoming ones. pub username: String, pub expires_at: Option, pub created_at: Ts, #[serde(flatten)] pub settings: ShareSettings, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Shares { pub outgoing: Vec, pub incoming: Vec, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct User { pub id: i64, pub username: String, pub is_admin: bool, pub created_at: Ts, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NewUser { pub username: String, pub password: String, pub is_admin: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SetRole { pub is_admin: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ResetPassword { pub password: String, } /// Upper bound for one upload, so one request cannot hold the database for long. pub const MAX_BATCH: usize = 1000; /// Coarsest share precision. pub const MAX_PRECISION_M: u32 = 100_000; /// Longest time range one track request may cover. pub const MAX_TRACK_SECS: Ts = 31 * 86400;