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