Add GPS trip reports and route maps
This commit is contained in:
+46
-14
@@ -8,6 +8,7 @@ import type { CarTelemetryRecorder } from "./car-telemetry.js";
|
||||
import type { CompanionDatabase } from "./database.js";
|
||||
import { hashToken, randomToken } from "./security.js";
|
||||
import { collectSystemStatus } from "./status.js";
|
||||
import { TripStore } from "./trips.js";
|
||||
|
||||
const pairingSchema = z.object({
|
||||
code: z.string().regex(/^\d{6}$/),
|
||||
@@ -71,6 +72,8 @@ export function registerMobileRoutes(
|
||||
carTelemetry: CarTelemetryRecorder,
|
||||
requireWebUser: (request: FastifyRequest, reply: FastifyReply) => boolean
|
||||
): void {
|
||||
const trips = new TripStore(database);
|
||||
|
||||
app.post("/api/mobile/pairing", async (request, reply) => {
|
||||
if (!requireWebUser(request, reply)) return;
|
||||
const code = String(randomInt(100_000, 1_000_000));
|
||||
@@ -94,6 +97,22 @@ export function registerMobileRoutes(
|
||||
return { devices };
|
||||
});
|
||||
|
||||
app.get("/api/trips", async (request, reply) => {
|
||||
if (!requireWebUser(request, reply)) return;
|
||||
return trips.list();
|
||||
});
|
||||
|
||||
app.get<{ Params: { id: string } }>("/api/trips/:id", async (request, reply) => {
|
||||
if (!requireWebUser(request, reply)) return;
|
||||
const id = Number(request.params.id);
|
||||
if (!Number.isInteger(id) || id < 1) {
|
||||
return reply.code(400).send({ error: { code: "INVALID_TRIP", message: "That trip is invalid" } });
|
||||
}
|
||||
const trip = trips.detail(id);
|
||||
if (!trip) return reply.code(404).send({ error: { code: "TRIP_NOT_FOUND", message: "Trip not found" } });
|
||||
return trip;
|
||||
});
|
||||
|
||||
app.get("/api/mobile/bluetooth", async (request, reply) => {
|
||||
if (!requireWebUser(request, reply)) return;
|
||||
return readBluetoothStatus();
|
||||
@@ -170,20 +189,33 @@ export function registerMobileRoutes(
|
||||
const parsed = locationSchema.safeParse(request.body);
|
||||
if (!parsed.success) return reply.code(400).send({ error: { code: "INVALID_PHONE_LOCATION", message: "The phone location was invalid" } });
|
||||
const value = parsed.data;
|
||||
database.prepare(
|
||||
`INSERT INTO mobile_phone_state
|
||||
(device_id, received_at, captured_at, latitude, longitude, accuracy_meters, altitude_meters,
|
||||
speed_kph, bearing_degrees, battery_percent, network_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(device_id) DO UPDATE SET
|
||||
received_at = excluded.received_at, captured_at = excluded.captured_at,
|
||||
latitude = excluded.latitude, longitude = excluded.longitude, accuracy_meters = excluded.accuracy_meters,
|
||||
altitude_meters = excluded.altitude_meters, speed_kph = excluded.speed_kph,
|
||||
bearing_degrees = excluded.bearing_degrees, battery_percent = excluded.battery_percent,
|
||||
network_type = excluded.network_type`
|
||||
).run(device.id, new Date().toISOString(), value.capturedAt, value.latitude, value.longitude, value.accuracyMeters,
|
||||
value.altitudeMeters ?? null, value.speedKph ?? null, value.bearingDegrees ?? null,
|
||||
value.batteryPercent ?? null, value.networkType ?? null);
|
||||
return { accepted: true };
|
||||
const trip = database.transaction(() => {
|
||||
database.prepare(
|
||||
`INSERT INTO mobile_phone_state
|
||||
(device_id, received_at, captured_at, latitude, longitude, accuracy_meters, altitude_meters,
|
||||
speed_kph, bearing_degrees, battery_percent, network_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(device_id) DO UPDATE SET
|
||||
received_at = excluded.received_at, captured_at = excluded.captured_at,
|
||||
latitude = excluded.latitude, longitude = excluded.longitude, accuracy_meters = excluded.accuracy_meters,
|
||||
altitude_meters = excluded.altitude_meters, speed_kph = excluded.speed_kph,
|
||||
bearing_degrees = excluded.bearing_degrees, battery_percent = excluded.battery_percent,
|
||||
network_type = excluded.network_type`
|
||||
).run(device.id, new Date().toISOString(), value.capturedAt, value.latitude, value.longitude, value.accuracyMeters,
|
||||
value.altitudeMeters ?? null, value.speedKph ?? null, value.bearingDegrees ?? null,
|
||||
value.batteryPercent ?? null, value.networkType ?? null);
|
||||
return trips.record({
|
||||
deviceId: device.id,
|
||||
capturedAt: value.capturedAt,
|
||||
latitude: value.latitude,
|
||||
longitude: value.longitude,
|
||||
accuracyMeters: value.accuracyMeters,
|
||||
altitudeMeters: value.altitudeMeters ?? null,
|
||||
speedKph: value.speedKph ?? null,
|
||||
bearingDegrees: value.bearingDegrees ?? null,
|
||||
phoneBatteryPercent: value.batteryPercent ?? null
|
||||
});
|
||||
})();
|
||||
return { accepted: true, trip };
|
||||
});
|
||||
|
||||
app.post<{ Params: { id: string } }>("/internal/mobile/shortcuts/:id/launch", async (request, reply) => {
|
||||
|
||||
Reference in New Issue
Block a user