lib.rs
⎇
Raw
1//! JSON types shared by the server, the CLI and the web UI.
2
3use serde::{Deserialize, Serialize};
4
5/// Unix seconds.
6pub type Ts = i64;
7
8#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
9pub struct Point {
10 pub ts: Ts,
11 pub lat: f64,
12 pub lon: f64,
13 /// Metres.
14 #[serde(default)]
15 pub acc: Option<f32>,
16 /// Metres.
17 #[serde(default)]
18 pub alt: Option<f32>,
19 /// m/s.
20 #[serde(default)]
21 pub speed: Option<f32>,
22 /// Degrees.
23 #[serde(default)]
24 pub bearing: Option<f32>,
25 /// Percent.
26 #[serde(default)]
27 pub battery: Option<u8>,
28}
29
30/// Someone whose position the caller may see. Always includes the caller.
31#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
32pub struct Person {
33 pub id: i64,
34 pub username: String,
35 /// The visible devices that have a position, newest first.
36 pub devices: Vec<PersonDevice>,
37 pub trail: Trail,
38 /// Positions are rounded to about this many metres. 0 means exact.
39 pub precision_m: u32,
40}
41
42impl Person {
43 /// The device with the newest position. Its position is the person's position.
44 pub fn last(&self) -> Option<&PersonDevice> {
45 self.devices.first()
46 }
47}
48
49#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
50pub struct PersonDevice {
51 pub id: i64,
52 pub name: String,
53 pub last: Point,
54}
55
56#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
57pub struct Me {
58 pub id: i64,
59 pub username: String,
60 pub is_admin: bool,
61 pub has_password: bool,
62 /// Sign-in needs the password and a passkey. Otherwise either one is enough.
63 pub two_factor: bool,
64 /// The user's own limit. None means the server limit.
65 pub retention_days: Option<i64>,
66 /// The server limit. None means points are kept forever.
67 pub max_retention_days: Option<i64>,
68 /// The address browsers use, if the server knows it.
69 pub public_url: Option<String>,
70}
71
72#[derive(Serialize, Deserialize, Clone, Debug)]
73pub struct SetupStatus {
74 /// True while no user exists. The first account becomes the admin.
75 pub needed: bool,
76}
77
78#[derive(Serialize, Deserialize, Clone, Debug)]
79pub struct Credentials {
80 pub username: String,
81 pub password: String,
82}
83
84#[derive(Serialize, Deserialize, Clone, Debug, Default)]
85pub struct Login {
86 #[serde(default)]
87 pub username: String,
88 pub password: String,
89}
90
91/// Reply to a sign-in step. `ok` means the session cookie is set.
92#[derive(Serialize, Deserialize, Clone, Debug, Default)]
93pub struct LoginResult {
94 pub ok: bool,
95 /// The password was right. The account also needs a passkey.
96 #[serde(default)]
97 pub passkey_challenge: Option<Challenge>,
98}
99
100/// The first leg of a WebAuthn ceremony.
101#[derive(Serialize, Deserialize, Clone, Debug)]
102pub struct Challenge {
103 pub state_id: String,
104 /// The options for `navigator.credentials`, as JSON.
105 pub options: String,
106}
107
108/// The second leg of a WebAuthn ceremony.
109#[derive(Serialize, Deserialize, Clone, Debug)]
110pub struct ChallengeAnswer {
111 pub state_id: String,
112 /// The browser's `PublicKeyCredential.toJSON()`, as JSON.
113 pub credential: String,
114 /// A label for a new passkey. Ignored when signing in.
115 #[serde(default)]
116 pub name: String,
117}
118
119#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
120pub struct Passkey {
121 pub id: i64,
122 pub name: String,
123 pub created_at: Ts,
124 pub last_used_at: Option<Ts>,
125}
126
127#[derive(Serialize, Deserialize, Clone, Debug)]
128pub struct ChangePassword {
129 /// Required when the account has a password.
130 pub old: Option<String>,
131 pub new: String,
132}
133
134#[derive(Serialize, Deserialize, Clone, Debug)]
135pub struct SetTwoFactor {
136 pub enabled: bool,
137}
138
139#[derive(Serialize, Deserialize, Clone, Debug)]
140pub struct SetRetention {
141 /// None means the server limit.
142 pub days: Option<i64>,
143}
144
145#[derive(Serialize, Deserialize, Clone, Debug)]
146pub struct NewDevice {
147 pub name: String,
148}
149
150#[derive(Serialize, Deserialize, Clone, Debug)]
151pub struct DeviceToken {
152 pub token: String,
153}
154
155/// Starts pairing an app. The web UI sends it for the signed-in user.
156#[derive(Serialize, Deserialize, Clone, Debug)]
157pub struct PairBegin {
158 /// SHA-256 in hex of a secret that only the app knows.
159 pub challenge: String,
160 pub name: String,
161}
162
163/// A one-time code for the app, valid 5 minutes.
164#[derive(Serialize, Deserialize, Clone, Debug)]
165pub struct PairCode {
166 pub code: String,
167}
168
169/// The app exchanges the code and its secret for a device token.
170#[derive(Serialize, Deserialize, Clone, Debug)]
171pub struct PairFinish {
172 pub code: String,
173 pub verifier: String,
174}
175
176#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
177pub struct Device {
178 pub id: i64,
179 pub name: String,
180 /// The one device that every browser with the web UI uploads as. It has no token.
181 pub web: bool,
182 pub created_at: Ts,
183 pub last_seen_at: Option<Ts>,
184}
185
186/// Reply to a point upload.
187#[derive(Serialize, Deserialize, Clone, Debug)]
188pub struct Uploaded {
189 /// Points that were new. Duplicates of stored points are ignored.
190 pub stored: usize,
191 /// Invalid points, for example with a timestamp far in the future. They are dropped, so they cannot block a client's queue.
192 pub skipped: usize,
193}
194
195#[derive(Serialize, Deserialize, Clone, Debug)]
196pub struct NewShare {
197 pub viewer: String,
198 /// None means no expiry.
199 pub expires_at: Option<Ts>,
200 #[serde(flatten)]
201 pub settings: ShareSettings,
202}
203
204/// How much of the history a viewer sees besides the current position.
205#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Default)]
206#[serde(rename_all = "snake_case")]
207pub enum Trail {
208 None,
209 /// Points from this time on.
210 Since(Ts),
211 #[default]
212 All,
213}
214
215/// What a share or guest link shows. Sharing again with the same viewer replaces these.
216#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
217pub struct ShareSettings {
218 /// The owner's device ids. None means all devices, including ones added later.
219 #[serde(default)]
220 pub devices: Option<Vec<i64>>,
221 #[serde(default)]
222 pub trail: Trail,
223 /// Rounds positions to about this many metres. 0 means exact.
224 #[serde(default)]
225 pub precision_m: u32,
226}
227
228#[derive(Serialize, Deserialize, Clone, Debug)]
229pub struct NewLink {
230 /// A label for the owner, for example whom the link is for.
231 pub name: String,
232 pub expires_at: Option<Ts>,
233 #[serde(flatten)]
234 pub settings: ShareSettings,
235 /// Guests must enter it before they see anything.
236 #[serde(default)]
237 pub password: Option<String>,
238}
239
240/// A guest link: anyone with the token can see what it shows.
241#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
242pub struct Link {
243 pub id: i64,
244 pub name: String,
245 pub token: String,
246 pub expires_at: Option<Ts>,
247 pub created_at: Ts,
248 pub has_password: bool,
249 #[serde(flatten)]
250 pub settings: ShareSettings,
251}
252
253/// Identifies a guest request.
254#[derive(Serialize, Deserialize, Clone, Debug)]
255pub struct GuestAuth {
256 pub token: String,
257 /// From GuestKey. Needed when the link has a password.
258 #[serde(default)]
259 pub key: Option<String>,
260}
261
262#[derive(Serialize, Deserialize, Clone, Debug)]
263pub struct GuestUnlock {
264 pub token: String,
265 pub password: String,
266}
267
268/// Proves the link password. It stops working when the link is deleted.
269#[derive(Serialize, Deserialize, Clone, Debug)]
270pub struct GuestKey {
271 pub key: String,
272}
273
274#[derive(Serialize, Deserialize, Clone, Debug)]
275pub struct GuestView {
276 pub expires_at: Option<Ts>,
277 pub person: Person,
278}
279
280#[derive(Serialize, Deserialize, Clone, Debug)]
281pub struct GuestTrack {
282 #[serde(flatten)]
283 pub auth: GuestAuth,
284 pub device: i64,
285 pub from: Ts,
286 pub to: Ts,
287}
288
289#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
290pub struct Share {
291 pub id: i64,
292 /// The other party: the viewer for outgoing shares, the owner for incoming ones.
293 pub username: String,
294 pub expires_at: Option<Ts>,
295 pub created_at: Ts,
296 #[serde(flatten)]
297 pub settings: ShareSettings,
298}
299
300#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
301pub struct Shares {
302 pub outgoing: Vec<Share>,
303 pub incoming: Vec<Share>,
304}
305
306#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
307pub struct User {
308 pub id: i64,
309 pub username: String,
310 pub is_admin: bool,
311 pub created_at: Ts,
312}
313
314#[derive(Serialize, Deserialize, Clone, Debug)]
315pub struct NewUser {
316 pub username: String,
317 pub password: String,
318 pub is_admin: bool,
319}
320
321#[derive(Serialize, Deserialize, Clone, Debug)]
322pub struct SetRole {
323 pub is_admin: bool,
324}
325
326#[derive(Serialize, Deserialize, Clone, Debug)]
327pub struct ResetPassword {
328 pub password: String,
329}
330
331/// Upper bound for one upload, so one request cannot hold the database for long.
332pub const MAX_BATCH: usize = 1000;
333
334/// Coarsest share precision.
335pub const MAX_PRECISION_M: u32 = 100_000;
336
337/// Longest time range one track request may cover.
338pub const MAX_TRACK_SECS: Ts = 31 * 86400;
339