Files
Aram Chia Sarafian a25c94093b Update Jest and tests
Move constants to separate file
Fix loop compatibility
2025-07-13 20:39:19 -07:00

131 lines
3.7 KiB
TypeScript

import { NextResponse } from 'next/server';
import { LIBRARY_LOCATIONS } from '../../utils/library-locations';
// Force dynamic rendering to prevent caching
export const dynamic = 'force-dynamic';
export const revalidate = 0;
// Library branch locations are now imported from a shared file
interface BibItem {
branchName: string;
availability: {
statusType: string;
};
}
interface BiblioResponse {
entities: {
bibItems: Record<string, BibItem>;
};
}
interface LocationData {
name: string;
lat: number;
lng: number;
available: boolean;
passType: string;
}
async function fetchAvailabilityData(itemNumber: string): Promise<BibItem[]> {
try {
const response = await fetch(
`https://gateway.bibliocommons.com/v2/libraries/sandiego/bibs/${itemNumber}/availability?locale=en-US`,
{
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; LibraryChecker/1.0)',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: BiblioResponse = await response.json();
return Object.values(data.entities.bibItems || {});
} catch (error) {
console.error(`Error fetching data for ${itemNumber}:`, error);
return [];
}
}
export async function GET() {
try {
// Fetch both types of passes
const [passItems, backpackItems] = await Promise.all([
fetchAvailabilityData('S161C1690437'), // Pure parking pass
fetchAvailabilityData('S161C1805116'), // Backpack with pass
]);
const locationMap = new Map<string, LocationData>();
// Process parking passes
passItems.forEach(item => {
const location = LIBRARY_LOCATIONS[item.branchName];
if (location && item.branchName) {
const isAvailable = item.availability?.statusType === 'AVAILABLE';
const existing = locationMap.get(item.branchName);
if (existing) {
// Update existing entry - if either item type is available, mark as available
existing.available = existing.available || isAvailable;
existing.passType = 'Both';
} else {
locationMap.set(item.branchName, {
name: item.branchName,
lat: location.lat,
lng: location.lng,
available: isAvailable,
passType: 'Pass Only',
});
}
}
});
// Process backpack items
backpackItems.forEach(item => {
const location = LIBRARY_LOCATIONS[item.branchName];
if (location && item.branchName) {
const isAvailable = item.availability?.statusType === 'AVAILABLE';
const existing = locationMap.get(item.branchName);
if (existing) {
// Update existing entry - if either item type is available, mark as available
existing.available = existing.available || isAvailable;
existing.passType = 'Both';
} else {
locationMap.set(item.branchName, {
name: item.branchName,
lat: location.lat,
lng: location.lng,
available: isAvailable,
passType: 'Backpack Only',
});
}
}
});
const locations = Array.from(locationMap.values());
const response = NextResponse.json({
locations,
lastUpdated: new Date().toISOString(),
});
// Prevent caching to ensure fresh data
response.headers.set('Cache-Control', 'no-cache, no-store, must-revalidate');
response.headers.set('Pragma', 'no-cache');
response.headers.set('Expires', '0');
return response;
} catch (error) {
console.error('Error in API route:', error);
return NextResponse.json(
{ error: 'Failed to fetch availability data' },
{ status: 500 }
);
}
}