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 /// Set when a passkey sign-in asked for the password as the second step.
90 #[serde(default)]
91 pub state_id: Option<String>,
92}
93
94/// Reply to a sign-in step. `ok` means the session cookie is set.
95#[derive(Serialize, Deserialize, Clone, Debug, Default)]
96pub struct LoginResult {
97 pub ok: bool,
98 /// The password was right. The account also needs a passkey.
99 #[serde(default)]
100 pub passkey_challenge: Option<Challenge>,
101 /// The passkey was right. The account also needs its password, sent with this state_id.
102 #[serde(default)]
103 pub password_required: Option<String>,
104}
105
106/// The first leg of a WebAuthn ceremony.
107#[derive(Serialize, Deserialize, Clone, Debug)]
108pub struct Challenge {
109 pub state_id: String,
110 /// The options for `navigator.credentials`, as JSON.
111 pub options: String,
112}
113
114/// The second leg of a WebAuthn ceremony.
115#[derive(Serialize, Deserialize, Clone, Debug)]
116pub struct ChallengeAnswer {
117 pub state_id: String,
118 /// The browser's `PublicKeyCredential.toJSON()`, as JSON.
119 pub credential: String,
120 /// A label for a new passkey. Ignored when signing in.
121 #[serde(default)]
122 pub name: String,
123}
124
125#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
126pub struct Passkey {
127 pub id: i64,
128 pub name: String,
129 pub created_at: Ts,
130 pub last_used_at: Option<Ts>,
131}
132
133#[derive(Serialize, Deserialize, Clone, Debug)]
134pub struct ChangePassword {
135 /// Required when the account has a password.
136 pub old: Option<String>,
137 pub new: String,
138}
139
140#[derive(Serialize, Deserialize, Clone, Debug)]
141pub struct SetTwoFactor {
142 pub enabled: bool,
143}
144
145#[derive(Serialize, Deserialize, Clone, Debug)]
146pub struct SetRetention {
147 /// None means the server limit.
148 pub days: Option<i64>,
149}
150
151#[derive(Serialize, Deserialize, Clone, Debug)]
152pub struct NewDevice {
153 pub name: String,
154}
155
156#[derive(Serialize, Deserialize, Clone, Debug)]
157pub struct DeviceToken {
158 pub token: String,
159}
160
161/// Starts pairing an app. The web UI sends it for the signed-in user.
162#[derive(Serialize, Deserialize, Clone, Debug)]
163pub struct PairBegin {
164 /// SHA-256 in hex of a secret that only the app knows.
165 pub challenge: String,
166 pub name: String,
167}
168
169/// A one-time code for the app, valid 5 minutes.
170#[derive(Serialize, Deserialize, Clone, Debug)]
171pub struct PairCode {
172 pub code: String,
173}
174
175/// The app exchanges the code and its secret for a device token.
176#[derive(Serialize, Deserialize, Clone, Debug)]
177pub struct PairFinish {
178 pub code: String,
179 pub verifier: String,
180}
181
182#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
183pub struct Device {
184 pub id: i64,
185 pub name: String,
186 /// The one device that every browser with the web UI uploads as. It has no token.
187 pub web: bool,
188 pub created_at: Ts,
189 pub last_seen_at: Option<Ts>,
190}
191
192/// Reply to a point upload.
193#[derive(Serialize, Deserialize, Clone, Debug)]
194pub struct Uploaded {
195 /// Points that were new. Duplicates of stored points are ignored.
196 pub stored: usize,
197 /// Invalid points, for example with a timestamp far in the future. They are dropped, so they cannot block a client's queue.
198 pub skipped: usize,
199}
200
201#[derive(Serialize, Deserialize, Clone, Debug)]
202pub struct NewShare {
203 pub viewer: String,
204 /// None means no expiry.
205 pub expires_at: Option<Ts>,
206 #[serde(flatten)]
207 pub settings: ShareSettings,
208}
209
210/// How much of the history a viewer sees besides the current position.
211#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Default)]
212#[serde(rename_all = "snake_case")]
213pub enum Trail {
214 None,
215 /// Points from this time on.
216 Since(Ts),
217 #[default]
218 All,
219}
220
221/// What a share or guest link shows. Sharing again with the same viewer replaces these.
222#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
223pub struct ShareSettings {
224 /// The owner's device ids. None means all devices, including ones added later.
225 #[serde(default)]
226 pub devices: Option<Vec<i64>>,
227 #[serde(default)]
228 pub trail: Trail,
229 /// Rounds positions to about this many metres. 0 means exact.
230 #[serde(default)]
231 pub precision_m: u32,
232}
233
234#[derive(Serialize, Deserialize, Clone, Debug)]
235pub struct NewLink {
236 /// A label for the owner, for example whom the link is for.
237 pub name: String,
238 pub expires_at: Option<Ts>,
239 #[serde(flatten)]
240 pub settings: ShareSettings,
241 /// Guests must enter it before they see anything.
242 #[serde(default)]
243 pub password: Option<String>,
244}
245
246/// A guest link: anyone with the token can see what it shows.
247#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
248pub struct Link {
249 pub id: i64,
250 pub name: String,
251 pub token: String,
252 pub expires_at: Option<Ts>,
253 pub created_at: Ts,
254 pub has_password: bool,
255 #[serde(flatten)]
256 pub settings: ShareSettings,
257}
258
259/// Identifies a guest request.
260#[derive(Serialize, Deserialize, Clone, Debug)]
261pub struct GuestAuth {
262 pub token: String,
263 /// From GuestKey. Needed when the link has a password.
264 #[serde(default)]
265 pub key: Option<String>,
266}
267
268#[derive(Serialize, Deserialize, Clone, Debug)]
269pub struct GuestUnlock {
270 pub token: String,
271 pub password: String,
272}
273
274/// Proves the link password. It stops working when the link is deleted.
275#[derive(Serialize, Deserialize, Clone, Debug)]
276pub struct GuestKey {
277 pub key: String,
278}
279
280#[derive(Serialize, Deserialize, Clone, Debug)]
281pub struct GuestView {
282 pub expires_at: Option<Ts>,
283 pub person: Person,
284}
285
286#[derive(Serialize, Deserialize, Clone, Debug)]
287pub struct GuestTrack {
288 #[serde(flatten)]
289 pub auth: GuestAuth,
290 pub device: i64,
291 pub from: Ts,
292 pub to: Ts,
293}
294
295#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
296pub struct Share {
297 pub id: i64,
298 /// The other party: the viewer for outgoing shares, the owner for incoming ones.
299 pub username: String,
300 pub expires_at: Option<Ts>,
301 pub created_at: Ts,
302 #[serde(flatten)]
303 pub settings: ShareSettings,
304}
305
306#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
307pub struct Shares {
308 pub outgoing: Vec<Share>,
309 pub incoming: Vec<Share>,
310}
311
312#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
313pub struct User {
314 pub id: i64,
315 pub username: String,
316 pub is_admin: bool,
317 pub created_at: Ts,
318}
319
320#[derive(Serialize, Deserialize, Clone, Debug)]
321pub struct NewUser {
322 pub username: String,
323 pub password: String,
324 pub is_admin: bool,
325}
326
327#[derive(Serialize, Deserialize, Clone, Debug)]
328pub struct SetRole {
329 pub is_admin: bool,
330}
331
332#[derive(Serialize, Deserialize, Clone, Debug)]
333pub struct ResetPassword {
334 pub password: String,
335}
336
337/// Upper bound for one upload, so one request cannot hold the database for long.
338pub const MAX_BATCH: usize = 1000;
339
340/// Coarsest share precision.
341pub const MAX_PRECISION_M: u32 = 100_000;
342
343/// Longest time range one track request may cover.
344pub const MAX_TRACK_SECS: Ts = 31 * 86400;
345