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