Files
location-mqtt-tracker-app/app/api/locations/route.ts
2025-11-24 20:33:15 +00:00

115 lines
3.9 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import type { LocationResponse } from "@/types/location";
import { locationDb, Location, deviceDb, userDb } from "@/lib/db";
import { auth } from "@/lib/auth";
/**
* GET /api/locations
*
* Fetches location data from local SQLite cache.
* The MQTT subscriber automatically writes new locations to the cache.
*
* Query parameters:
* - username: Filter by device tracker ID
* - timeRangeHours: Filter by time range (e.g., 1, 3, 6, 12, 24)
* - startTime: Custom range start (ISO string)
* - endTime: Custom range end (ISO string)
* - limit: Maximum number of records (default: 1000)
*/
export async function GET(request: NextRequest) {
try {
// Check authentication
const session = await auth();
if (!session?.user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Get user's allowed device IDs for filtering locations
const userId = (session.user as any).id;
const role = (session.user as any).role;
const sessionUsername = session.user.name || '';
// Get list of device IDs the user is allowed to access
const userDeviceIds = userDb.getAllowedDeviceIds(userId, role, sessionUsername);
// If user has no devices, return empty response
if (userDeviceIds.length === 0) {
return NextResponse.json({
success: true,
current: null,
history: [],
total_points: 0,
last_updated: new Date().toISOString(),
});
}
const searchParams = request.nextUrl.searchParams;
const username = searchParams.get('username') || undefined;
const timeRangeHours = searchParams.get('timeRangeHours')
? parseInt(searchParams.get('timeRangeHours')!, 10)
: undefined;
const startTime = searchParams.get('startTime') || undefined;
const endTime = searchParams.get('endTime') || undefined;
const limit = searchParams.get('limit')
? parseInt(searchParams.get('limit')!, 10)
: 1000;
// Read from local SQLite with filters
let locations = locationDb.findMany({
user_id: 0, // Always filter for MQTT devices
username,
timeRangeHours,
startTime,
endTime,
limit,
});
// Filter locations to only include user's devices
locations = locations.filter(loc => loc.username && userDeviceIds.includes(loc.username));
// Normalize locations: Ensure speed, battery, and display_time are correct
locations = locations.map(loc => {
// Generate display_time if missing or regenerate from timestamp
const displayTime = loc.display_time || new Date(loc.timestamp).toLocaleString('de-DE', {
timeZone: 'Europe/Berlin',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
return {
...loc,
display_time: displayTime,
speed: loc.speed !== undefined && loc.speed !== null ? Number(loc.speed) : null,
battery: loc.battery !== undefined && loc.battery !== null ? Number(loc.battery) : null,
};
});
// Get actual total count from database (not limited by 'limit' parameter)
const stats = locationDb.getStats();
// Return data in standard format
const response: LocationResponse = {
success: true,
current: locations.length > 0 ? locations[0] : null,
history: locations,
total_points: stats.total, // Use actual total from DB, not limited results
last_updated: locations.length > 0 ? locations[0].timestamp : new Date().toISOString(),
};
return NextResponse.json(response);
} catch (error) {
console.error("Error fetching locations:", error);
return NextResponse.json(
{
error: "Failed to fetch locations",
details: error instanceof Error ? error.message : "Unknown error"
},
{ status: 500 }
);
}
}