O3JTKE2JLRNBU2ULVFRH4BWADTJK3BADHPMLBNEPDW2FOIRDKS6AC /*** Protected API route to fetch call types for a species** @route GET /api/call-types* @authentication Required* @param {string} speciesId - Required query parameter specifying the species ID* @returns {Object} Response containing:* - data: Array of call type objects for the specified species* @error 400 - If speciesId is missing* @error 403 - If user doesn't have READ permission for the species* @description Returns all active call types associated with the specified species*/callTypes.get("/", authenticate, async (c) => {try {const jwtPayload = (c as unknown as { jwtPayload: JWTPayload }).jwtPayload;const userId = jwtPayload.sub;const speciesId = c.req.query("speciesId");// Validate required parameterif (!speciesId) {return c.json({error: "Missing required query parameter: speciesId"}, 400);}const db = createDatabase(c.env);// Check if species exists and get its associated datasetsconst associatedDatasets = await db.select({ datasetId: speciesDataset.datasetId }).from(speciesDataset).where(eq(speciesDataset.speciesId, speciesId));if (associatedDatasets.length === 0) {return c.json({error: "Species not found or not associated with any dataset"}, 404);}// Check if user has READ permission on at least one associated datasetlet hasPermission = false;for (const assoc of associatedDatasets) {if (await checkUserPermission(db, userId, assoc.datasetId, 'READ')) {hasPermission = true;break;}}if (!hasPermission) {return c.json({error: "You don't have permission to view call types for this species"}, 403);}
// Fetch call types for the speciesconst callTypesResult = await db.select({id: callType.id,speciesId: callType.speciesId,label: callType.label,createdAt: callType.createdAt,active: callType.active,}).from(callType).where(eq(callType.speciesId, speciesId)).orderBy(callType.label);// Filter to only active call typesconst activeCallTypes = callTypesResult.filter(ct => ct.active);return c.json({data: activeCallTypes});} catch (error) {console.error("Error fetching call types:", error);return c.json({error: "Failed to fetch call types",details: error instanceof Error ? error.message : String(error),},500);}});
const filesResponse = await fetch(`/api/files?clusterId=${encodeURIComponent(clusterId)}&pageSize=1000`, {
console.log('Fetching files for clusterId:', clusterId);const filesResponse = await fetch(`/api/files?clusterId=${encodeURIComponent(clusterId)}&pageSize=500`, {
throw new Error('Failed to fetch cluster files');
const errorText = await filesResponse.text();console.error('Files API error:', filesResponse.status, errorText);throw new Error(`Failed to fetch cluster files: ${filesResponse.status} ${errorText}`);
// Map base file names to file IDs
// Create a map of database file base names to file IDs for efficient lookupconst dbFileMap = new Map<string, { id: string; fileName: string }>();for (const dbFile of availableFiles) {// Skip hidden files (starting with .)if (dbFile.fileName.startsWith('.')) {continue;}// Extract base name from database file name (remove extension and path)let dbFileName = dbFile.fileName;// Remove path if present (take last part after /)if (dbFileName.includes('/')) {dbFileName = dbFileName.split('/').pop() || dbFileName;}// Remove extension (.wav or .WAV)dbFileName = dbFileName.replace(/\.(wav|WAV)$/i, '');dbFileMap.set(dbFileName, dbFile);}console.log('Database files available:', Array.from(dbFileMap.keys()).slice(0, 10), `... (${dbFileMap.size} total)`);console.log('Selection base names to match:', fileBaseNames.slice(0, 10), `... (${fileBaseNames.length} total)`);// Show first few examples for comparisonconsole.log('First 5 database files:', Array.from(dbFileMap.keys()).slice(0, 5));console.log('First 5 selection base names:', fileBaseNames.slice(0, 5));// Map base file names from selections to file IDs
const found = availableFiles.find((f: { id: string; fileName: string }) => {// Extract base name from file name (remove extension and path)const fileName = f.fileName.replace(/\.(wav|WAV)$/, '');return fileName === baseName;});if (found) {fileMap.set(baseName, found.id);
const dbFile = dbFileMap.get(baseName);if (dbFile) {fileMap.set(baseName, dbFile.id);console.log(`✓ Mapped: ${baseName} -> ${dbFile.id} (${dbFile.fileName})`);} else {console.log(`✗ No match found for: ${baseName}`);