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 RegisterDevice {
153 pub username: String,
154 pub password: String,
155 pub name: String,
156}
157
158#[derive(Serialize, Deserialize, Clone, Debug)]
159pub struct NewDevice {
160 pub name: String,
161}
162
163#[derive(Serialize, Deserialize, Clone, Debug)]
164pub struct DeviceToken {
165 pub token: String,
166}
167
168/// Starts pairing an app. The web UI sends it for the signed-in user.
169#[derive(Serialize, Deserialize, Clone, Debug)]
170pub struct PairBegin {
171 /// SHA-256 in hex of a secret that only the app knows.
172 pub challenge: String,
173 pub name: String,
174}
175
176/// A one-time code for the app, valid 5 minutes.
177#[derive(Serialize, Deserialize, Clone, Debug)]
178pub struct PairCode {
179 pub code: String,
180}
181
182/// The app exchanges the code and its secret for a device token.
183#[derive(Serialize, Deserialize, Clone, Debug)]
184pub struct PairFinish {
185 pub code: String,
186 pub verifier: String,
187}
188
189#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
190pub struct Device {
191 pub id: i64,
192 pub name: String,
193 /// The one device that every browser with the web UI uploads as. It has no token.
194 pub web: bool,
195 pub created_at: Ts,
196 pub last_seen_at: Option<Ts>,
197}
198
199/// Reply to a point upload.
200#[derive(Serialize, Deserialize, Clone, Debug)]
201pub struct Uploaded {
202 /// Points that were new. Duplicates of stored points are ignored.
203 pub stored: usize,
204 pub people: Vec<Person>,
205}
206
207#[derive(Serialize, Deserialize, Clone, Debug)]
208pub struct NewShare {
209 pub viewer: String,
210 /// None means no expiry.
211 pub expires_at: Option<Ts>,
212 #[serde(flatten)]
213 pub settings: ShareSettings,
214}
215
216/// How much of the history a viewer sees besides the current position.
217#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Default)]
218#[serde(rename_all = "snake_case")]
219pub enum Trail {
220 None,
221 /// Points from this time on.
222 Since(Ts),
223 #[default]
224 All,
225}
226
227/// What a share or guest link shows. Sharing again with the same viewer replaces these.
228#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
229pub struct ShareSettings {
230 /// The owner's device ids. None means all devices, including ones added later.
231 #[serde(default)]
232 pub devices: Option<Vec<i64>>,
233 #[serde(default)]
234 pub trail: Trail,
235 /// Rounds positions to about this many metres. 0 means exact.
236 #[serde(default)]
237 pub precision_m: u32,
238}
239
240#[derive(Serialize, Deserialize, Clone, Debug)]
241pub struct NewLink {
242 /// A label for the owner, for example whom the link is for.
243 pub name: String,
244 pub expires_at: Option<Ts>,
245 #[serde(flatten)]
246 pub settings: ShareSettings,
247 /// Guests must enter it before they see anything.
248 #[serde(default)]
249 pub password: Option<String>,
250}
251
252/// A guest link: anyone with the token can see what it shows.
253#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
254pub struct Link {
255 pub id: i64,
256 pub name: String,
257 pub token: String,
258 pub expires_at: Option<Ts>,
259 pub created_at: Ts,
260 pub has_password: bool,
261 #[serde(flatten)]
262 pub settings: ShareSettings,
263}
264
265/// Identifies a guest request.
266#[derive(Serialize, Deserialize, Clone, Debug)]
267pub struct GuestAuth {
268 pub token: String,
269 /// From GuestKey. Needed when the link has a password.
270 #[serde(default)]
271 pub key: Option<String>,
272}
273
274#[derive(Serialize, Deserialize, Clone, Debug)]
275pub struct GuestUnlock {
276 pub token: String,
277 pub password: String,
278}
279
280/// Proves the link password. It stops working when the link is deleted.
281#[derive(Serialize, Deserialize, Clone, Debug)]
282pub struct GuestKey {
283 pub key: String,
284}
285
286#[derive(Serialize, Deserialize, Clone, Debug)]
287pub struct GuestView {
288 pub expires_at: Option<Ts>,
289 pub person: Person,
290}
291
292#[derive(Serialize, Deserialize, Clone, Debug)]
293pub struct GuestTrack {
294 #[serde(flatten)]
295 pub auth: GuestAuth,
296 pub device: i64,
297 pub from: Ts,
298 pub to: Ts,
299}
300
301#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
302pub struct Share {
303 pub id: i64,
304 /// The other party: the viewer for outgoing shares, the owner for incoming ones.
305 pub username: String,
306 pub expires_at: Option<Ts>,
307 pub created_at: Ts,
308 #[serde(flatten)]
309 pub settings: ShareSettings,
310}
311
312#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
313pub struct Shares {
314 pub outgoing: Vec<Share>,
315 pub incoming: Vec<Share>,
316}
317
318#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
319pub struct User {
320 pub id: i64,
321 pub username: String,
322 pub is_admin: bool,
323 pub created_at: Ts,
324}
325
326#[derive(Serialize, Deserialize, Clone, Debug)]
327pub struct NewUser {
328 pub username: String,
329 pub password: String,
330 pub is_admin: bool,
331}
332
333#[derive(Serialize, Deserialize, Clone, Debug)]
334pub struct SetRole {
335 pub is_admin: bool,
336}
337
338#[derive(Serialize, Deserialize, Clone, Debug)]
339pub struct ResetPassword {
340 pub password: String,
341}
342
343/// Upper bound for one upload, so one request cannot hold the database for long.
344pub const MAX_BATCH: usize = 1000;
345
346/// Coarsest share precision.
347pub const MAX_PRECISION_M: u32 = 100_000;
348
349/// Longest time range one track request may cover.
350pub const MAX_TRACK_SECS: Ts = 31 * 86400;
351