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